{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# PySpark Cookbook\n",
    "\n",
    "### Tomasz Drabas, Denny Lee\n",
    "#### Version: 0.1\n",
    "#### Date: 1/15/2018"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Some data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Starting Spark application\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<table>\n",
       "<tr><th>ID</th><th>YARN Application ID</th><th>Kind</th><th>State</th><th>Spark UI</th><th>Driver log</th><th>Current session?</th></tr><tr><td>0</td><td>None</td><td>pyspark3</td><td>idle</td><td></td><td></td><td>✔</td></tr></table>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "SparkSession available as 'spark'.\n"
     ]
    }
   ],
   "source": [
    "dirty_data = spark.createDataFrame([\n",
    "          (1,'Porsche','Boxster S','Turbo',2.5,4,22,None)\n",
    "        , (2,'Aston Martin','Vanquish','Aspirated',6.0,12,16,None)\n",
    "        , (3,'Porsche','911 Carrera 4S Cabriolet','Turbo',3.0,6,24,None)\n",
    "        , (3,'General Motors','SPARK ACTIV','Aspirated',1.4,None,32,None)\n",
    "        , (5,'BMW','COOPER S HARDTOP 2 DOOR','Turbo',2.0,4,26,None)\n",
    "        , (6,'BMW','330i','Turbo',2.0,None,27,None)\n",
    "        , (7,'BMW','440i Coupe','Turbo',3.0,6,23,None)\n",
    "        , (8,'BMW','440i Coupe','Turbo',3.0,6,23,None)\n",
    "        , (9,'Mercedes-Benz',None,None,None,None,27,None)\n",
    "        , (10,'Mercedes-Benz','CLS 550','Turbo',4.7,8,21,79231)\n",
    "        , (11,'Volkswagen','GTI','Turbo',2.0,4,None,None)\n",
    "        , (12,'Ford Motor Company','FUSION AWD','Turbo',2.7,6,20,None)\n",
    "        , (13,'Nissan','Q50 AWD RED SPORT','Turbo',3.0,6,22,None)\n",
    "        , (14,'Nissan','Q70 AWD','Aspirated',5.6,8,18,None)\n",
    "        , (15,'Kia','Stinger RWD','Turbo',2.0,4,25,None)\n",
    "        , (16,'Toyota','CAMRY HYBRID LE','Aspirated',2.5,4,46,None)\n",
    "        , (16,'Toyota','CAMRY HYBRID LE','Aspirated',2.5,4,46,None)\n",
    "        , (18,'FCA US LLC','300','Aspirated',3.6,6,23,None)\n",
    "        , (19,'Hyundai','G80 AWD','Turbo',3.3,6,20,None)\n",
    "        , (20,'Hyundai','G80 AWD','Turbo',3.3,6,20,None)\n",
    "        , (21,'BMW','X5 M','Turbo',4.4,8,18,121231)\n",
    "        , (22,'GE','K1500 SUBURBAN 4WD','Aspirated',5.3,8,18,None)\n",
    "    ], ['Id','Manufacturer','Model','EngineType','Displacement',\n",
    "        'Cylinders','FuelEconomy','MSRP'])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Handling duplicates"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Exact duplicates"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(22, 21)"
     ]
    }
   ],
   "source": [
    "# do we have any rows that are duplicated?\n",
    "dirty_data.count(), dirty_data.distinct().count()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "+---+------------+---------------+----------+------------+---------+-----------+----+-----+\n",
      "| Id|Manufacturer|          Model|EngineType|Displacement|Cylinders|FuelEconomy|MSRP|count|\n",
      "+---+------------+---------------+----------+------------+---------+-----------+----+-----+\n",
      "| 16|      Toyota|CAMRY HYBRID LE| Aspirated|         2.5|        4|         46|null|    2|\n",
      "+---+------------+---------------+----------+------------+---------+-----------+----+-----+"
     ]
    }
   ],
   "source": [
    "# what row is duplicated?\n",
    "(\n",
    "    dirty_data\n",
    "    .groupby(dirty_data.columns)\n",
    "    .count()\n",
    "    .filter('count > 1')\n",
    "    .show()\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "21"
     ]
    }
   ],
   "source": [
    "# remove the duplicated rows\n",
    "full_removed = dirty_data.dropDuplicates()\n",
    "full_removed.count()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Only ID differs"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(21, 19)"
     ]
    }
   ],
   "source": [
    "# count of rows\n",
    "no_ids = (\n",
    "    full_removed\n",
    "    .select([col for col in full_removed.columns if col != 'Id'])\n",
    ")\n",
    "\n",
    "no_ids.count(), no_ids.distinct().count()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "+------------+----------+----------+------------+---------+-----------+----+-----+\n",
      "|Manufacturer|     Model|EngineType|Displacement|Cylinders|FuelEconomy|MSRP|count|\n",
      "+------------+----------+----------+------------+---------+-----------+----+-----+\n",
      "|         BMW|440i Coupe|     Turbo|         3.0|        6|         23|null|    2|\n",
      "|     Hyundai|   G80 AWD|     Turbo|         3.3|        6|         20|null|    2|\n",
      "+------------+----------+----------+------------+---------+-----------+----+-----+"
     ]
    }
   ],
   "source": [
    "# what row is duplicated?\n",
    "(\n",
    "    full_removed\n",
    "    .groupby([col for col in full_removed.columns if col != 'Id'])\n",
    "    .count()\n",
    "    .filter('count > 1')\n",
    "    .show()\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "# remove the duplicated record\n",
    "id_removed = full_removed.dropDuplicates(\n",
    "    subset = [col for col in full_removed.columns if col != 'Id']\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "19"
     ]
    }
   ],
   "source": [
    "# count\n",
    "id_removed.count()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Duplicated IDs"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "+----------+------------------+\n",
      "|CountOfIDs|CountOfDistinctIDs|\n",
      "+----------+------------------+\n",
      "|        19|                18|\n",
      "+----------+------------------+"
     ]
    }
   ],
   "source": [
    "# are there any duplicated IDs?\n",
    "import pyspark.sql.functions as fn\n",
    "\n",
    "id_removed.agg(\n",
    "      fn.count('Id').alias('CountOfIDs')\n",
    "    , fn.countDistinct('Id').alias('CountOfDistinctIDs')\n",
    ").show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "+---+-----+\n",
      "| Id|count|\n",
      "+---+-----+\n",
      "|  3|    2|\n",
      "+---+-----+"
     ]
    }
   ],
   "source": [
    "# what's duplicated?\n",
    "(\n",
    "    id_removed\n",
    "    .groupby('Id')\n",
    "    .count()\n",
    "    .filter('count > 1')\n",
    "    .show()\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "+---+--------------+--------------------+----------+------------+---------+-----------+----+\n",
      "| Id|  Manufacturer|               Model|EngineType|Displacement|Cylinders|FuelEconomy|MSRP|\n",
      "+---+--------------+--------------------+----------+------------+---------+-----------+----+\n",
      "|  3|General Motors|         SPARK ACTIV| Aspirated|         1.4|     null|         32|null|\n",
      "|  3|       Porsche|911 Carrera 4S Ca...|     Turbo|         3.0|        6|         24|null|\n",
      "+---+--------------+--------------------+----------+------------+---------+-----------+----+"
     ]
    }
   ],
   "source": [
    "(\n",
    "    id_removed\n",
    "    .filter('Id = 3')\n",
    "    .show()\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "+-------------+------------------+--------------------+----------+------------+---------+-----------+------+\n",
      "|           Id|      Manufacturer|               Model|EngineType|Displacement|Cylinders|FuelEconomy|  MSRP|\n",
      "+-------------+------------------+--------------------+----------+------------+---------+-----------+------+\n",
      "|   8589934592|    General Motors|         SPARK ACTIV| Aspirated|         1.4|     null|         32|  null|\n",
      "| 188978561024|     Mercedes-Benz|             CLS 550|     Turbo|         4.7|        8|         21| 79231|\n",
      "| 197568495616|     Mercedes-Benz|                null|      null|        null|     null|         27|  null|\n",
      "| 206158430208|Ford Motor Company|          FUSION AWD|     Turbo|         2.7|        6|         20|  null|\n",
      "| 438086664192|               BMW|COOPER S HARDTOP ...|     Turbo|         2.0|        4|         26|  null|\n",
      "| 523986010112|      Aston Martin|            Vanquish| Aspirated|         6.0|       12|         16|  null|\n",
      "| 721554505728|        Volkswagen|                 GTI|     Turbo|         2.0|        4|       null|  null|\n",
      "| 764504178688|               Kia|         Stinger RWD|     Turbo|         2.0|        4|         25|  null|\n",
      "| 919123001344|               BMW|                330i|     Turbo|         2.0|     null|         27|  null|\n",
      "| 944892805120|           Porsche|           Boxster S|     Turbo|         2.5|        4|         22|  null|\n",
      "| 970662608896|        FCA US LLC|                 300| Aspirated|         3.6|        6|         23|  null|\n",
      "|1030792151040|           Hyundai|             G80 AWD|     Turbo|         3.3|        6|         20|  null|\n",
      "|1039382085632|               BMW|          440i Coupe|     Turbo|         3.0|        6|         23|  null|\n",
      "|1116691496960|            Nissan|   Q50 AWD RED SPORT|     Turbo|         3.0|        6|         22|  null|\n",
      "|1211180777472|               BMW|                X5 M|     Turbo|         4.4|        8|         18|121231|\n",
      "|1331439861760|            Nissan|             Q70 AWD| Aspirated|         5.6|        8|         18|  null|\n",
      "|1606317768704|           Porsche|911 Carrera 4S Ca...|     Turbo|         3.0|        6|         24|  null|\n",
      "|1614907703296|            Toyota|     CAMRY HYBRID LE| Aspirated|         2.5|        4|         46|  null|\n",
      "|1700807049216|                GE|  K1500 SUBURBAN 4WD| Aspirated|         5.3|        8|         18|  null|\n",
      "+-------------+------------------+--------------------+----------+------------+---------+-----------+------+"
     ]
    }
   ],
   "source": [
    "new_id = (\n",
    "    id_removed\n",
    "    .select(\n",
    "        [fn.monotonically_increasing_id().alias('Id')] + \n",
    "        [col for col in id_removed.columns if col != 'Id'])\n",
    ")\n",
    "\n",
    "new_id.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Handling missing observations"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "### Missing observations per row"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "+------------+------------+\n",
      "|          Id|CountMissing|\n",
      "+------------+------------+\n",
      "|197568495616|           5|\n",
      "|  8589934592|           2|\n",
      "|919123001344|           2|\n",
      "|721554505728|           2|\n",
      "+------------+------------+"
     ]
    }
   ],
   "source": [
    "(\n",
    "    spark.createDataFrame(\n",
    "        new_id.rdd.map(\n",
    "           lambda row: (\n",
    "                 row['Id']\n",
    "               , sum([c == None for c in row])\n",
    "           )\n",
    "        )\n",
    "        .filter(lambda el: el[1] > 1)\n",
    "        .collect()\n",
    "        ,['Id', 'CountMissing']\n",
    "    )\n",
    "    .orderBy('CountMissing', ascending=False)\n",
    "    .show()\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "+------------+-------------+-----+----------+------------+---------+-----------+----+\n",
      "|          Id| Manufacturer|Model|EngineType|Displacement|Cylinders|FuelEconomy|MSRP|\n",
      "+------------+-------------+-----+----------+------------+---------+-----------+----+\n",
      "|197568495616|Mercedes-Benz| null|      null|        null|     null|         27|null|\n",
      "+------------+-------------+-----+----------+------------+---------+-----------+----+"
     ]
    }
   ],
   "source": [
    "(\n",
    "    new_id\n",
    "    .where('Id == 197568495616')\n",
    "    .show()\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(19, 18)"
     ]
    }
   ],
   "source": [
    "merc_out = new_id.dropna(thresh=4)\n",
    "new_id.count(), merc_out.count()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "+---+------------+-----+----------+------------+---------+-----------+----+\n",
      "| Id|Manufacturer|Model|EngineType|Displacement|Cylinders|FuelEconomy|MSRP|\n",
      "+---+------------+-----+----------+------------+---------+-----------+----+\n",
      "+---+------------+-----+----------+------------+---------+-----------+----+"
     ]
    }
   ],
   "source": [
    "(\n",
    "    merc_out\n",
    "    .where('Id == 197568495616')\n",
    "    .show()\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Missing observations per column"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "MSRP_miss 0.8888888888888888\n",
      "Cylinders_miss 0.11111111111111116\n",
      "FuelEconomy_miss 0.05555555555555558\n",
      "Id_miss 0.0\n",
      "Manufacturer_miss 0.0\n",
      "Model_miss 0.0\n",
      "EngineType_miss 0.0\n",
      "Displacement_miss 0.0"
     ]
    }
   ],
   "source": [
    "for k, v in sorted(\n",
    "    merc_out.agg(*[\n",
    "               (1 - (fn.count(c) / fn.count('*')))\n",
    "                    .alias(c + '_miss')\n",
    "               for c in merc_out.columns\n",
    "           ])\n",
    "        .collect()[0]\n",
    "        .asDict()\n",
    "        .items()\n",
    "    , key=lambda el: el[1]\n",
    "    , reverse=True\n",
    "):\n",
    "    print(k, v)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "+-------------+------------------+--------------------+----------+------------+---------+-----------+\n",
      "|           Id|      Manufacturer|               Model|EngineType|Displacement|Cylinders|FuelEconomy|\n",
      "+-------------+------------------+--------------------+----------+------------+---------+-----------+\n",
      "|   8589934592|    General Motors|         SPARK ACTIV| Aspirated|         1.4|     null|         32|\n",
      "| 188978561024|     Mercedes-Benz|             CLS 550|     Turbo|         4.7|        8|         21|\n",
      "| 206158430208|Ford Motor Company|          FUSION AWD|     Turbo|         2.7|        6|         20|\n",
      "| 438086664192|               BMW|COOPER S HARDTOP ...|     Turbo|         2.0|        4|         26|\n",
      "| 523986010112|      Aston Martin|            Vanquish| Aspirated|         6.0|       12|         16|\n",
      "| 721554505728|        Volkswagen|                 GTI|     Turbo|         2.0|        4|       null|\n",
      "| 764504178688|               Kia|         Stinger RWD|     Turbo|         2.0|        4|         25|\n",
      "| 919123001344|               BMW|                330i|     Turbo|         2.0|     null|         27|\n",
      "| 944892805120|           Porsche|           Boxster S|     Turbo|         2.5|        4|         22|\n",
      "| 970662608896|        FCA US LLC|                 300| Aspirated|         3.6|        6|         23|\n",
      "|1030792151040|           Hyundai|             G80 AWD|     Turbo|         3.3|        6|         20|\n",
      "|1039382085632|               BMW|          440i Coupe|     Turbo|         3.0|        6|         23|\n",
      "|1116691496960|            Nissan|   Q50 AWD RED SPORT|     Turbo|         3.0|        6|         22|\n",
      "|1211180777472|               BMW|                X5 M|     Turbo|         4.4|        8|         18|\n",
      "|1331439861760|            Nissan|             Q70 AWD| Aspirated|         5.6|        8|         18|\n",
      "|1606317768704|           Porsche|911 Carrera 4S Ca...|     Turbo|         3.0|        6|         24|\n",
      "|1614907703296|            Toyota|     CAMRY HYBRID LE| Aspirated|         2.5|        4|         46|\n",
      "|1700807049216|                GE|  K1500 SUBURBAN 4WD| Aspirated|         5.3|        8|         18|\n",
      "+-------------+------------------+--------------------+----------+------------+---------+-----------+"
     ]
    }
   ],
   "source": [
    "no_MSRP = merc_out.select([col for col in new_id.columns if col != 'MSRP'])\n",
    "no_MSRP.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Sparse missing observations"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'FuelEconomy': 1.4957485048359973, 'Cylinders': 1.8353365984789105}"
     ]
    }
   ],
   "source": [
    "multipliers = (\n",
    "    no_MSRP\n",
    "    .agg(\n",
    "          fn.mean(\n",
    "              fn.col('FuelEconomy') / \n",
    "              (\n",
    "                  fn.col('Displacement') * fn.col('Cylinders')\n",
    "              )\n",
    "          ).alias('FuelEconomy')\n",
    "        , fn.mean(\n",
    "            fn.col('Cylinders') / \n",
    "            fn.col('Displacement')\n",
    "        ).alias('Cylinders')\n",
    "    )\n",
    ").toPandas().to_dict('records')[0]\n",
    "\n",
    "multipliers"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "+-------------+------------------+--------------------+----------+------------+---------+------------------+\n",
      "|           Id|      Manufacturer|               Model|EngineType|Displacement|Cylinders|       FuelEconomy|\n",
      "+-------------+------------------+--------------------+----------+------------+---------+------------------+\n",
      "|   8589934592|    General Motors|         SPARK ACTIV| Aspirated|         1.4|        2| 4.188095813540793|\n",
      "| 188978561024|     Mercedes-Benz|             CLS 550|     Turbo|         4.7|        8|              21.0|\n",
      "| 206158430208|Ford Motor Company|          FUSION AWD|     Turbo|         2.7|        5|16.666666666666668|\n",
      "| 438086664192|               BMW|COOPER S HARDTOP ...|     Turbo|         2.0|        4|              26.0|\n",
      "| 523986010112|      Aston Martin|            Vanquish| Aspirated|         6.0|       12|              16.0|\n",
      "| 721554505728|        Volkswagen|                 GTI|     Turbo|         2.0|        4|11.965988038687978|\n",
      "| 764504178688|               Kia|         Stinger RWD|     Turbo|         2.0|        4|              25.0|\n",
      "| 919123001344|               BMW|                330i|     Turbo|         2.0|        3| 8.974491029015983|\n",
      "| 944892805120|           Porsche|           Boxster S|     Turbo|         2.5|        4|              22.0|\n",
      "| 970662608896|        FCA US LLC|                 300| Aspirated|         3.6|        6|              23.0|\n",
      "|1030792151040|           Hyundai|             G80 AWD|     Turbo|         3.3|        6|              20.0|\n",
      "|1039382085632|               BMW|          440i Coupe|     Turbo|         3.0|        6|23.000000000000004|\n",
      "|1116691496960|            Nissan|   Q50 AWD RED SPORT|     Turbo|         3.0|        6|21.999999999999996|\n",
      "|1211180777472|               BMW|                X5 M|     Turbo|         4.4|        8|              18.0|\n",
      "|1331439861760|            Nissan|             Q70 AWD| Aspirated|         5.6|        8|              18.0|\n",
      "|1606317768704|           Porsche|911 Carrera 4S Ca...|     Turbo|         3.0|        6|              24.0|\n",
      "|1614907703296|            Toyota|     CAMRY HYBRID LE| Aspirated|         2.5|        4|              46.0|\n",
      "|1700807049216|                GE|  K1500 SUBURBAN 4WD| Aspirated|         5.3|        8|              18.0|\n",
      "+-------------+------------------+--------------------+----------+------------+---------+------------------+"
     ]
    }
   ],
   "source": [
    "imputed = (\n",
    "    no_MSRP\n",
    "    .withColumn('FuelEconomy', fn.col('FuelEconomy')   / fn.col('Displacement') / fn.col('Cylinders'))\n",
    "    .withColumn('Cylinders',   fn.col('Cylinders')   / fn.col('Displacement'))\n",
    "    .fillna(multipliers)\n",
    "    .withColumn('Cylinders',   (fn.col('Cylinders')   * fn.col('Displacement')).cast('integer'))\n",
    "    .withColumn('FuelEconomy', fn.col('FuelEconomy') * fn.col('Displacement') * fn.col('Cylinders'))\n",
    ")\n",
    "\n",
    "imputed.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Handling outliers"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "+-------------+--------------+-----------+-------------+\n",
      "|           id|Displacement_o|Cylinders_o|FuelEconomy_o|\n",
      "+-------------+--------------+-----------+-------------+\n",
      "|   8589934592|         false|      false|         true|\n",
      "| 188978561024|         false|      false|        false|\n",
      "| 206158430208|         false|      false|        false|\n",
      "| 438086664192|         false|      false|        false|\n",
      "| 523986010112|         false|      false|        false|\n",
      "| 721554505728|         false|      false|        false|\n",
      "| 764504178688|         false|      false|        false|\n",
      "| 919123001344|         false|      false|        false|\n",
      "| 944892805120|         false|      false|        false|\n",
      "| 970662608896|         false|      false|        false|\n",
      "|1030792151040|         false|      false|        false|\n",
      "|1039382085632|         false|      false|        false|\n",
      "|1116691496960|         false|      false|        false|\n",
      "|1211180777472|         false|      false|        false|\n",
      "|1331439861760|         false|      false|        false|\n",
      "|1606317768704|         false|      false|        false|\n",
      "|1614907703296|         false|      false|         true|\n",
      "|1700807049216|         false|      false|        false|\n",
      "+-------------+--------------+-----------+-------------+"
     ]
    }
   ],
   "source": [
    "features = ['Displacement', 'Cylinders', 'FuelEconomy']\n",
    "quantiles = [0.25, 0.75]\n",
    "\n",
    "cut_off_points = []\n",
    "\n",
    "for feature in features:\n",
    "    quants = imputed.approxQuantile(feature, quantiles, 0.05)\n",
    "    \n",
    "    IQR = quants[1] - quants[0]\n",
    "    cut_off_points.append((feature, [\n",
    "        quants[0] - 1.5 * IQR,\n",
    "        quants[1] + 1.5 * IQR,\n",
    "    ]))\n",
    "    \n",
    "cut_off_points = dict(cut_off_points)\n",
    "\n",
    "outliers = imputed.select(*['id'] + [\n",
    "       (\n",
    "           (imputed[f] < cut_off_points[f][0]) |\n",
    "           (imputed[f] > cut_off_points[f][1])\n",
    "       ).alias(f + '_o') for f in features\n",
    "  ])\n",
    "outliers.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "+-------------+--------------+---------------+-----------------+\n",
      "|           Id|  Manufacturer|          Model|      FuelEconomy|\n",
      "+-------------+--------------+---------------+-----------------+\n",
      "|   8589934592|General Motors|    SPARK ACTIV|4.188095813540793|\n",
      "|1614907703296|        Toyota|CAMRY HYBRID LE|             46.0|\n",
      "+-------------+--------------+---------------+-----------------+"
     ]
    }
   ],
   "source": [
    "with_outliers_flag = imputed.join(outliers, on='Id')\n",
    "\n",
    "(\n",
    "    with_outliers_flag\n",
    "    .filter('FuelEconomy_o')\n",
    "    .select('Id', 'Manufacturer', 'Model', 'FuelEconomy')\n",
    "    .show()\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [],
   "source": [
    "no_outliers = (\n",
    "    with_outliers_flag\n",
    "    .filter('!FuelEconomy_o')\n",
    "    .select(imputed.columns)\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Exploring descriptive statistics"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "+-------+-----------------+-----------------+------------------+\n",
      "|summary|     Displacement|        Cylinders|       FuelEconomy|\n",
      "+-------+-----------------+-----------------+------------------+\n",
      "|  count|               16|               16|                16|\n",
      "|   mean|          3.44375|            6.125|19.600446608398165|\n",
      "| stddev|1.354975399530683|2.276693508870558| 4.666647767373751|\n",
      "|    min|              2.0|                3| 8.974491029015983|\n",
      "|    max|              6.0|               12|              26.0|\n",
      "+-------+-----------------+-----------------+------------------+"
     ]
    }
   ],
   "source": [
    "descriptive_stats = no_outliers.describe(features)\n",
    "descriptive_stats.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "+-------+--------------------+------------+-----+----------+-----------------+-----------------+------------------+\n",
      "|summary|                  Id|Manufacturer|Model|EngineType|     Displacement|        Cylinders|       FuelEconomy|\n",
      "+-------+--------------------+------------+-----+----------+-----------------+-----------------+------------------+\n",
      "|  count|                  16|          16|   16|        16|               16|               16|                16|\n",
      "|   mean|    9.19659872256E11|        null|300.0|      null|          3.44375|            6.125|19.600446608398165|\n",
      "| stddev|4.396778949583304E11|        null|  NaN|      null|1.354975399530683|2.276693508870558| 4.666647767373751|\n",
      "|    min|        188978561024|Aston Martin|  300| Aspirated|              2.0|                3| 8.974491029015983|\n",
      "|    max|       1700807049216|  Volkswagen| X5 M|     Turbo|              6.0|               12|              26.0|\n",
      "+-------+--------------------+------------+-----+----------+-----------------+-----------------+------------------+"
     ]
    }
   ],
   "source": [
    "descriptive_stats_all = no_outliers.describe()\n",
    "descriptive_stats_all.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "+---------+-----+------------------+------------------+------------------+-------------------+\n",
      "|Cylinders|Count|           MPG_avg|          Disp_avg|         MPG_stdev|         Disp_stdev|\n",
      "+---------+-----+------------------+------------------+------------------+-------------------+\n",
      "|        3|    1| 8.974491029015983|               2.0|               NaN|                NaN|\n",
      "|        4|    4|21.241497009671995|             2.125| 6.413009924998989|0.24999999999999994|\n",
      "|        5|    1|16.666666666666668|               2.7|               NaN|                NaN|\n",
      "|        6|    5|              22.4|3.1799999999999997|1.5165750888103104|0.26832815729997467|\n",
      "|        8|    4|             18.75|               5.0|               1.5| 0.5477225575051655|\n",
      "|       12|    1|              16.0|               6.0|               NaN|                NaN|\n",
      "+---------+-----+------------------+------------------+------------------+-------------------+"
     ]
    }
   ],
   "source": [
    "(\n",
    "    no_outliers\n",
    "    .select(features)\n",
    "    .groupBy('Cylinders')\n",
    "    .agg(*[\n",
    "          fn.count('*').alias('Count')\n",
    "        , fn.mean('FuelEconomy').alias('MPG_avg')\n",
    "        , fn.mean('Displacement').alias('Disp_avg')\n",
    "        , fn.stddev('FuelEconomy').alias('MPG_stdev')\n",
    "        , fn.stddev('Displacement').alias('Disp_stdev')\n",
    "    ])\n",
    "    .orderBy('Cylinders')\n",
    ").show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Computing correlations"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0.9381829964408109"
     ]
    }
   ],
   "source": [
    "(\n",
    "    no_outliers\n",
    "    .corr('Cylinders', 'Displacement')\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "+------------+------------+------------------+--------------------+\n",
      "|      Column|Displacement|         Cylinders|         FuelEconomy|\n",
      "+------------+------------+------------------+--------------------+\n",
      "|Displacement|         1.0|0.9381829964408109|-0.10757908872387652|\n",
      "|   Cylinders|        null|               1.0|-0.04218546545035314|\n",
      "| FuelEconomy|        null|              null|                 1.0|\n",
      "+------------+------------+------------------+--------------------+"
     ]
    }
   ],
   "source": [
    "n_features = len(features)\n",
    "\n",
    "corr = []\n",
    "\n",
    "for i in range(0, n_features):\n",
    "    temp = [None] * i\n",
    "\n",
    "    for j in range(i, n_features):\n",
    "        temp.append(no_outliers.corr(features[i], features[j]))\n",
    "    corr.append([features[i]] + temp)\n",
    "\n",
    "correlations = spark.createDataFrame(corr, ['Column'] + features)\n",
    "\n",
    "correlations.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Drawing histograms"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [],
   "source": [
    "histogram_MPG = (\n",
    "    no_outliers\n",
    "    .select('FuelEconomy')\n",
    "    .rdd\n",
    "    .flatMap(lambda record: record)\n",
    "    .histogram(5)\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[8.974491029015983, 12.379592823212786, 15.78469461740959, 19.189796411606395, 22.594898205803197, 26.0]\n",
      "[2, 0, 5, 4, 5]\n",
      "([8.974491029015983, 12.379592823212786, 15.78469461740959, 19.189796411606395, 22.594898205803197, 26.0], [2, 0, 5, 4, 5])"
     ]
    }
   ],
   "source": [
    "for i in histogram_MPG:\n",
    "    print(i)\n",
    "    \n",
    "histogram_MPG"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[8.97,12.38)\n",
      "[12.38,15.78)\n",
      "[15.78,19.19)\n",
      "[19.19,22.59)\n",
      "[22.59,26.0)"
     ]
    }
   ],
   "source": [
    "for i in range(len(histogram_MPG[0])-1):\n",
    "    print('[' + str(round(histogram_MPG[0][i],2))\n",
    "        + ',' + str(round(histogram_MPG[0][i+1],2))\n",
    "          + ')'\n",
    "         )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {},
   "outputs": [],
   "source": [
    "(\n",
    "    spark\n",
    "    .createDataFrame(\n",
    "        [(bins, counts) \n",
    "         for bins, counts \n",
    "         in zip(\n",
    "             histogram_MPG[0], \n",
    "             histogram_MPG[1]\n",
    "         )]\n",
    "        , ['bins', 'counts']\n",
    "    )\n",
    "    .registerTempTable('histogram_MPG')\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%sql -o hist_MPG -q\n",
    "SELECT * FROM histogram_MPG"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Text(0.5,1,'Histogram of fuel economy')"
      ]
     },
     "execution_count": 34,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAArwAAAIZCAYAAABEa+weAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMS4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvNQv5yAAAIABJREFUeJzt3X2QVfV9+PHPXUAQkAVc5GERBCE1EJAoiFIJGFZsfGjVNDaaqNSH+FjHSI1KVdJRyFZhsFGMNA3Emjp5mMg4NrFJV4skwakIEhCNiEjViiIPoqJIdvf7+8Nxf25Y2EV277JfXq8ZZvbee+653/s9Z+++OXv23kJKKQUAAGSqpLUHAAAALUnwAgCQNcELAEDWBC8AAFkTvAAAZE3wAgCQNcEL7LUjjjgibr/99tYeRptSXV0dF110URx66KFRKBRi0aJFDS736quvxqRJk6JLly5RKBSadQy2G3CgErxARERMmTIlKioqGrytUCjEj370o7rLS5cujW9+85tNWu9vf/vbKBQKsX79+uYYZpv185//PB588MF45JFHYsOGDTFu3LgGl5s5c2Zs3LgxVqxYERs2bCjyKAHy1L61BwC0Pb169WrtIezWzp0746CDDmrtYezixRdfjPLy8t2G7ieXO+6442Lo0KFFGhlA/hzhBfban/5q/OGHH47Pf/7z0blz5+jevXscd9xx8cwzz8T69etj/PjxERExaNCgKBQKMXHixIiISCnFrFmzYvDgwXHQQQfFkUceGXfddVe9x9m8eXN85StfiS5dukTv3r3jlltuiQsvvLDekeiJEyfGxRdfHLfcckv07ds3ysvLIyLiwQcfjLFjx0ZpaWmUlZXFaaedFmvWrKm73/r166NQKMSDDz4Yp5xySnTu3DmOOuqoeOKJJ+L//u//4tRTT40uXbrEsGHD4je/+c0e56Ox5zJx4sS45ZZbYt26dVEoFOKII45ocD2FQiEee+yxmD9/fhQKhZgyZUrd9Z88wh4RUVFRUXd7xEenTHz729+OQYMGRadOnWL48OExb968PY67IWvXro0vf/nL0b179+jRo0dMnjw5Vq1aVW+ZZcuWxV/8xV9Et27domvXrnHcccfF//zP/9Tdfv/998ewYcOiY8eO0b9//7j55pujurq63nxccsklcdttt0WfPn2iZ8+eMWXKlNi+fXvdMk3ZP4444oi45ZZb4oorrojS0tI47LDD4p577okPP/ww/u7v/i569OgR5eXlcc8999Td58ILL4zJkyfv8rxPOumkevMJZCYBpJQuvPDCNGnSpAZvi4j0wAMP1F0eOHBguu2221JKKW3YsCF16NAh/dM//VNat25deu6559K///u/p5UrV6bq6ur08MMPp4hITz31VNqwYUPavHlzSimle+65J3Xq1CnNmzcvrVmzJn3ve99LHTt2TP/6r/9a9zhnnHFGGjp0aHr88cfTs88+m6ZMmZK6detWb5wTJkxIXbt2TZdddllavXp1WrlyZUoppfnz56dHHnkkrV27Ni1fvjydccYZaciQIenDDz9MKaX08ssvp4hIgwcPTgsXLkwvvPBCOvPMM1Pfvn3TpEmT0kMPPZReeOGFdPbZZ6f+/funnTt37nbuGnsumzdvTlOnTk1HHHFE2rBhQ9q4cWOD69mwYUM64YQT0nnnnZc2bNiQ3n777QbnP6WUJk2alC688MJ622/EiBHpV7/6VVq3bl368Y9/nEpLS+vN5ye3W0PeeOON1Lt373T55ZenlStXpj/84Q/p6quvTj179qwb87PPPps6d+6cvvrVr6alS5emNWvWpAcffDAtWbIkpZTSf/zHf6SSkpI0c+bM9MILL6Qf//jHqXv37unmm2+ut81KS0vTtddem55//vn06KOPptLS0nTrrbc2eU4/fj6lpaVp9uzZ6cUXX0y33XZbKhQK6Utf+lLddTNnzkyFQiGtXr06pZTSkiVLUqFQSOvWratbz9q1a1OhUEi//e1vdzs3QNsmeIGU0kfB1K5du9SlS5dd/u0peJcvX54iIr388ssNrvc3v/lNg7f3798/XX/99fWuu/baa9OgQYNSSimtWbMmRUSqqqqqu33nzp2pf//+uwTv0KFDU01NzR6f3+bNm1NE1EXNx8E7Z86cumWeeuqpFBFp1qxZddd9/PxWrVq123U39lxSSmn69OnpyCOP3OMYP34+F198cb3rGgvedevWpUKhkJ5//vl6y/zjP/5jOvroo+suNxa806dPT2PHjq13XW1tbRo8eHDdPH39619PI0eO3O18n3jiiekrX/lKvevuuuuu1KlTp7r/bEyYMCGNGDGi3jKXXXZZOv744+suN2VOBw4cmP7qr/6q7nJNTU065JBD0umnn17vuu7du6e777677roRI0akf/iHf6i7fOONN6Zhw4Y1+HyAPDilAagzduzYWLFixS7/9mTkyJFxyimnxOc+97k466yz4p//+Z/j1Vdf3eN93nnnnXjttdfiC1/4Qr3rJ0yYEOvXr4/3338/nnvuuYiIOP744+tu79ChQ4wePXqX9R177LFRUlL/5WzFihVx1llnxaBBg+KQQw6JAQMGRETE//7v/9Zb7uijj677uk+fPnXP6U+v27hx46d+Li3t6aefjpRSjB49Orp27Vr3b+bMmfHiiy82eT1Lly6NZcuW1VvHIYccEuvXr69bz7Jly2LSpEm7zPfHVq9e3eBc7NixI1566aW660aNGlVvmfLy8njzzTcjYu/m9JPbr6SkJHr16lVv+5WUlMRhhx1Wb/tddtllsWDBgqipqYnq6ur44Q9/GJdeemmT5ghom/zRGlDn4IMPjiFDhuzVfdq1axePPvpoLF26NKqqquLnP/953HjjjfGzn/0sTj/99D3e90/fdiul1OgyDenSpUu9y++//35Mnjw5TjzxxJg/f35dtA4fPjx27txZb9kOHTrs8lgNXVdbW7vHMTTluXxahUJhl/X98Y9/rPv647EtWbIkOnfuvMdx7UltbW1MmjSp3jmvHystLW3yOnc3F5+8/k//sLBQKOwyx02Z009uq4/v09B1n1z3+eefHzfccEP84he/iNra2ti6dWtccMEFe3xOQNvmCC+wzwqFQhx33HExbdq0WLx4cUyYMCEWLFgQEf8/bGpqauqW79atW/Tv3z+eeOKJeutZvHhxDBo0KDp37hzDhg2LiIgnn3yy7vbq6upYtmxZo+N5/vnn46233ooZM2bESSedFJ/97Gdj69atzRqhH2vKc9lXhx12WLz++ut1lz/88MO6I+ARHx3hjoh45ZVXYsiQIfX+HXnkkU1+nNGjR8fq1aujvLx8l/V8/M4cxx57bFRVVe32PwDDhw9vcC4OPvjgGDx4cJPG0dJz2q1bt/jqV78a3//+9+P73/9+fPnLX46ePXvu0zqB/ZsjvMA+WbJkSTz22GMxefLk6Nu3b7z44ouxcuXKuPjiiyMiYuDAgVFSUhK//OUv42/+5m+iY8eOUVpaGjfddFNMnTo1hg4dGhMnTozHH388vve978XcuXMjImLo0KFxxhlnxFVXXRXz5s2LXr16xezZs+Odd95p9AjjwIEDo2PHjnH33XfH1KlTY/369XHjjTc2+wc5fKyx57KvKioq4r777osvfOELccghh8SMGTPqHakeMmRIXHTRRXHppZfGHXfcESeccEJs3749li1bFm+99VbccMMNTXqcq6++On7wgx/EmWeeGTfffHMcfvjh8dprr8Wjjz4ap512WowbNy6+9a1vxdixY+NrX/taTJ06NXr06BHLly+P/v37xwknnBA33XRTnHHGGVFZWRlnn312rFixIr797W/H1KlT9+rt4lp6Ti+77LI44YQTIiLisccea5Z1AvsvwQvsk9LS0njyySdj7ty5sXXr1ujTp0987Wtfi1tuuSUiInr37h3f+c53orKyMq699toYP358LFq0KK644orYvn17zJw5M6688so4/PDDo7Kysi6UIyIWLFgQl112WXzpS1+Krl27xuWXXx4nn3xy7NixY49jKisrix/96Edx0003xfz58+Ozn/1s3HXXXTFp0qQWmYOmPJd9MWvWrLj00kvjlFNOidLS0pg2bVq89dZb9Zb5l3/5l5g9e3bMmDEj1q1bF926dYvhw4fH1Vdf3eTH6d27dzz55JMxbdq0OPvss+Odd96JPn36xPjx46Nv374RETFixIhYtGhRTJs2LSZMmBAlJSUxbNiwuPvuuyMi4tRTT4358+dHZWVl3HrrrdGrV6+48sorY/r06Xv1nFt6TseMGRMjRoyI999/PyZMmNAs6wT2X4XUEr/jA2gBNTU1cdRRR8Vf/uVfxuzZs1t7OLRh1dXVMXDgwLjuuuti6tSprT0coIU5wgvstxYvXhwbN26Mz3/+8/Huu+/GnDlzYv369T4ggE+ttrY2Nm7cGPPmzYv33nsvLrnkktYeElAEghfYb9XU1MTtt98ea9eujQ4dOsTnPve5+O///u8YMWJEaw+NNuqVV16JQYMGRd++fWPBggX13n0CyJdTGgAAyJq3JQMAIGuCFwCArAleAACy1mJ/tPbJTwUqprKysti0aVOrPPaByHwXj7kuLvNdPOa6uMx38ZjrltevX78mLecILwAAWRO8AABkTfACAJA1wQsAQNYELwAAWRO8AABkTfACAJA1wQsAQNYELwAAWRO8AABkTfACAJA1wQsAQNYELwAAWRO8AABkTfACAJA1wQsAQNYELwAAWWvflIWuuuqq6NSpU5SUlES7du2isrKypccFAADNoknBGxExffr06NatW0uOBQAAmp1TGgAAyFqTj/DOmDEjIiJOPvnkqKioaLEBAQBAcyqklFJjC23ZsiV69uwZ27Zti9tvvz3+9m//NoYNG1ZvmaqqqqiqqoqIiMrKyti5c2fLjLgR7du3j+rq6lZ57AOR+S4ecx3x5lnjWnsIfAq9Fy7Z4+327eLa3+fb93nb1Nj3eUs56KCDmrRck47w9uzZMyIiSktLY8yYMbF27dpdgreioqLekd9NmzY1dazNqqysrNUe+0BkvovHXNNWNbbf2reLy3zTElprn+rXr1+Tlmv0HN4dO3bEBx98UPf1ypUrY8CAAfs2OgAAKJJGj/Bu27YtZs2aFRERNTU1ceKJJ8aoUaNafGAAANAcGg3e3r17x5133lmMsQAAQLPztmQAAGRN8AIAkDXBCwBA1gQvAABZE7wAAGRN8AIAkDXBCwBA1gQvAABZE7wAAGRN8AIAkDXBCwBA1gQvAABZE7wAAGRN8AIAkDXBCwBA1gQvAABZE7wAAGRN8AIAkDXBCwBA1gQvAABZE7wAAGRN8AIAkDXBCwBA1gQvAABZE7wAAGRN8AIAkDXBCwBA1gQvAABZE7wAAGRN8AIAkDXBCwBA1gQvAABZE7wAAGRN8AIAkDXBCwBA1gQvAABZE7wAAGRN8AIAkDXBCwBA1gQvAABZE7wAAGRN8AIAkDXBCwBA1gQvAABZE7wAAGRN8AIAkDXBCwBA1gQvAABZE7wAAGRN8AIAkDXBCwBA1gQvAABZE7wAAGRN8AIAkDXBCwBA1gQvAABZE7wAAGRN8AIAkDXBCwBA1gQvAABZE7wAAGRN8AIAkDXBCwBA1gQvAABZE7wAAGRN8AIAkDXBCwBA1gQvAABZE7wAAGRN8AIAkDXBCwBA1gQvAABZE7wAAGRN8AIAkDXBCwBA1gQvAABZE7wAAGRN8AIAkDXBCwBA1gQvAABZE7wAAGRN8AIAkDXBCwBA1gQvAABZE7wAAGStycFbW1sb3/rWt6KysrIlxwMAAM2qycH7y1/+MsrLy1tyLAAA0OyaFLybN2+O5cuXx6RJk1p6PAAA0KzaN2WhH/7wh/H1r389Pvjgg90uU1VVFVVVVRERUVlZGWVlZc0zwr3Uvn37VnvsA5H5Lh5zHfFmaw+AT6Wx/XZP+/abZ41riSEd0IrxfdR74ZJPfV/f523T/v7zqdHgXbZsWZSWlsbgwYNj9erVu12uoqIiKioq6i5v2rSpeUa4l8rKylrtsQ9E5rt4zDVtVWP7rX07P7bngae1tnm/fv2atFyjwfvCCy/E008/Hc8880zs3LkzPvjgg/jud78b11xzzT4PEgAAWlqjwXveeefFeeedFxERq1evjkceeUTsAgDQZngfXgAAstakP1r72PDhw2P48OEtNRYAAGh2jvACAJA1wQsAQNYELwAAWRO8AABkTfACAJA1wQsAQNYELwAAWRO8AABkTfACAJA1wQsAQNYELwAAWRO8AABkTfACAJA1wQsAQNYELwAAWRO8AABkTfACAJA1wQsAQNYELwAAWRO8AABkTfACAJA1wQsAQNYELwAAWRO8AABkTfACAJA1wQsAQNYELwAAWRO8AABkTfACAJA1wQsAQNYELwAAWRO8AABkTfACAJA1wQsAQNYELwAAWRO8AABkTfACAJA1wQsAQNYELwAAWRO8AABkTfACAJA1wQsAQNYELwAAWRO8AABkTfACAJA1wQsAQNYELwAAWRO8AABkTfACAJA1wQsAQNYELwAAWRO8AABkTfACAJA1wQsAQNYELwAAWRO8AABkTfACAJA1wQsAQNYELwAAWRO8AABkTfACAJA1wQsAQNYELwAAWRO8AABkTfACAJA1wQsAQNYELwAAWRO8AABkTfACAJA1wQsAQNYELwAAWRO8AABkTfACAJA1wQsAQNYELwAAWRO8AABkTfACAJA1wQsAQNYELwAAWRO8AABkTfACAJA1wQsAQNYELwAAWRO8AABkrX1jC+zcuTOmT58e1dXVUVNTE8cff3ycc845xRgbAADss0aDt0OHDjF9+vTo1KlTVFdXx6233hqjRo2Kz3zmM8UYHwAA7JNGT2koFArRqVOniIioqamJmpqaKBQKLT4wAABoDo0e4Y2IqK2tjRtuuCHeeOONOOWUU2Lo0KG7LFNVVRVVVVUREVFZWRllZWXNO9Imat++fas99oHIfBePuY54s7UHwKfS2H67p33bNm+b9uW1yjZvm/b3n09NCt6SkpK48847Y/v27TFr1qx45ZVXYsCAAfWWqaioiIqKirrLmzZtat6RNlFZWVmrPfaByHwXj7mmrWpsv7Vv58f2PPC01jbv169fk5bbq3dp6NKlSwwbNixWrFjxqQYFAADF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E7wAAGRN8AIAkDXBCwBA1gQvAABZE7wAAGRN8AIAkDXBCwBA1gQvAABZE7wAAGRN8AIAkDXBCwBA1gQvAABZE7wAAGRN8AIAkDXBCwBA1gQvAABZE7wAAGRN8AIAkDXBCwBA1gQvAABZE7wAAGRN8AIAkDXBCwBA1gQvAABZE7wAAGRN8AIAkDXBCwBA1gQvAABZE7wAAGRN8AIAkDXBCwBA1gQvAABZE7wAAGRN8AIAkDXBCwBA1gQvAABZE7wAAGRN8AIAkDXBCwBA1to3ZaEVK1bEggULora2NiZNmhRnnnlmS48LAACaRaNHeGtra+MHP/hBTJs2LebMmRO/+93v4rXXXivG2AAAYJ81Grxr166NPn36RO/evaN9+/Yxbty4WLp0aTHGBgAA+6zR4N2yZUsceuihdZcPPfTQ2LJlS4sOCgAAmkuj5/CmlHa5rlAo7HJdVVVVVFVVRUREZWVl9OvXrxmGt5d+8XTxHxOKqFW+r/Ynvseztdt92zY/8NjmtIBGj/AeeuihsXnz5rrLmzdvjh49euyyXEVFRVRWVkZlZWXzjnAv3Xjjja36+Aca81085rq4zHfxmOviMt/FY673H40G75FHHhkbNmyIjRs3RnV1dSxZsiRGjx5djLEBAMA+a/SUhnbt2sVFF10UM2bMiNra2jjppJPi8MMPL8bYAABgnzXpfXiPOeaYOOaYY1p6LM2ioqKitYdwQDHfxWOui8t8F4+5Li7zXTzmev9RSA39VRoAAGTCRwsDAJC1Jp3SsL95/fXXY86cOXWXN27cGOecc06cdtppddetXr067rjjjjjssMMiImLs2LHx13/910Ufa1t17733xvLly6O0tDRmz54dERHvvfdezJkzJ956663o1atXfPOb34yuXbvuct9FixbFQw89FBERZ599dkycOLGYQ29zGprrBx54IJYtWxbt27eP3r17x5VXXhldunTZ5b5XXXVVdOrUKUpKSqJdu3at/i4pbUFD8/3Tn/40HnvssejWrVtERJx77rkNnsblY9b3TkNzPWfOnHj99dcjIuL999+Pzp07x5133rnLfe3be2fTpk0xd+7cePvtt6NQKERFRUWceuqpXrdbyO7m22v3fiy1cTU1NemSSy5JGzdurHf9s88+m77zne+00qjavtWrV6eXXnopXXfddXXXPfDAA2nhwoUppZQWLlyYHnjggV3u9+6776arrroqvfvuu/W+ZvcamusVK1ak6urqlNJH897QXKeU0pVXXpm2bdtWlHHmoqH5/slPfpIefvjhPd6vpqYmXX311emNN95If/zjH9Pf//3fp1dffbWlh9umNTTXn3T//fenn/3sZw3eZt/eO1u2bEkvvfRSSiml999/P11zzTXp1Vdf9brdQnY33167919t/pSGVatWRZ8+faJXr16tPZSsDBs2bJejAEuXLo0JEyZERMSECRMa/IjpFStWxMiRI6Nr167RtWvXGDlyZKxYsaIoY26rGprro48+Otq1axcREZ+fOI7eAAAEQ0lEQVT5zGd8umEzami+m8LHrO+9Pc11SimefPLJ+PM///MijypPPXr0iMGDB0dExMEHHxzl5eWxZcsWr9stZHfz7bV7/9UmT2n4pN/97ne7fcFcs2ZNXH/99dGjR484//zzvZ3aPtq2bVvdh4706NEj3nnnnV2W+dOPou7Zs6dv+H30+OOPx7hx43Z7+4wZMyIi4uSTT/YXwfvgV7/6VSxevDgGDx4cF1xwwS6h1tDHrL/44ovFHmY2nn/++SgtLY2+ffvudhn79qezcePGePnll2PIkCFet4vgk/P9SV679y9tOnirq6tj2bJlcd555+1y26BBg+Lee++NTp06xfLly+POO++M7373u60wShr6KGqa5qGHHop27drF+PHjG7z9tttui549e8a2bdvi9ttvj379+sWwYcOKPMq2b/LkyXXn+P/kJz+Jf/u3f4srr7yy3jKpiR+zTtPs6WBFhH3709qxY0fMnj07pkyZEp07d/7U67FvN83u5ttr9/6nTZ/S8Mwzz8SgQYOie/fuu9zWuXPn6NSpU0R89D7CNTU1Df7PlqYrLS2NrVu3RkTE1q1b6/7A55N69uxZ76Oot2zZ0uBHUdO4RYsWxbJly+Kaa67Z7Q+fnj17RsRH22bMmDGxdu3aYg4xG927d4+SkpIoKSmJSZMmxUsvvbTLMk39mHUaV1NTE0899dQej37Zt/dedXV1zJ49O8aPHx9jx46NCK/bLamh+Y7w2r2/atPBu6cjBG+//XbdEZm1a9dGbW1tHHLIIcUcXnZGjx4dTzzxREREPPHEEzFmzJhdlhk1alT8/ve/j/feey/ee++9+P3vfx+jRo0q9lDbvBUrVsTDDz8cN9xwQ3Ts2LHBZXbs2BEffPBB3dcrV66MAQMGFHOY2fg4CCIinnrqqQZPf/Ix681n1apV0a9fv3q/Rv8k+/beSynFfffdF+Xl5XH66afXXe91u2Xsbr69du+/2uwHT3z44YdxxRVXxD333FP3a4Rf//rXEfHRryf/8z//M379619Hu3bt4qCDDooLLrgg/uzP/qw1h9ym3HXXXfHcc8/Fu+++G6WlpXHOOefEmDFjYs6cObFp06YoKyuL6667Lrp27RovvfRS/Nd//VdcfvnlEfHReUsLFy6MiI/e3uakk05qzaey32torhcuXBjV1dV155EOHTo0vvGNb8SWLVti3rx5cdNNN8Wbb74Zs2bNioiPjpideOKJcfbZZ7fmU2kTGprv1atXx/r166NQKESvXr3iG9/4RvTo0aPefEdELF++PO6///66j1k333vW0Fx/8YtfjLlz58bQoUNj8uTJdcvat/fNH/7wh7j11ltjwIABdUcVzz333Bg6dKjX7Rawu/lesGCB1+79VJsNXgAAaIo2fUoDAAA0RvACAJA1wQsAQNYELwAAWRO8AABkTfACAJA1wQsAQNYELwAAWft/7DQSCrQcuQkAAAAASUVORK5CYII=\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x111ec2d30>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "%%local\n",
    "import matplotlib.pyplot as plt\n",
    "%matplotlib inline\n",
    "plt.style.use('ggplot')\n",
    "\n",
    "fig = plt.figure(figsize=(12,9))\n",
    "ax = fig.add_subplot(1, 1, 1)\n",
    "ax.bar(hist_MPG['bins'], hist_MPG['counts'], width=3)\n",
    "ax.set_title('Histogram of fuel economy')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "\n",
       "    <div class=\"bk-root\">\n",
       "        <a href=\"https://bokeh.pydata.org\" target=\"_blank\" class=\"bk-logo bk-logo-small bk-logo-notebook\"></a>\n",
       "        <span id=\"914b9e35-4d0e-443a-a9a9-4afc9375ec4c\">Loading BokehJS ...</span>\n",
       "    </div>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/javascript": [
       "\n",
       "(function(root) {\n",
       "  function now() {\n",
       "    return new Date();\n",
       "  }\n",
       "\n",
       "  var force = true;\n",
       "\n",
       "  if (typeof (root._bokeh_onload_callbacks) === \"undefined\" || force === true) {\n",
       "    root._bokeh_onload_callbacks = [];\n",
       "    root._bokeh_is_loading = undefined;\n",
       "  }\n",
       "\n",
       "  var JS_MIME_TYPE = 'application/javascript';\n",
       "  var HTML_MIME_TYPE = 'text/html';\n",
       "  var EXEC_MIME_TYPE = 'application/vnd.bokehjs_exec.v0+json';\n",
       "  var CLASS_NAME = 'output_bokeh rendered_html';\n",
       "\n",
       "  /**\n",
       "   * Render data to the DOM node\n",
       "   */\n",
       "  function render(props, node) {\n",
       "    var script = document.createElement(\"script\");\n",
       "    node.appendChild(script);\n",
       "  }\n",
       "\n",
       "  /**\n",
       "   * Handle when an output is cleared or removed\n",
       "   */\n",
       "  function handleClearOutput(event, handle) {\n",
       "    var cell = handle.cell;\n",
       "\n",
       "    var id = cell.output_area._bokeh_element_id;\n",
       "    var server_id = cell.output_area._bokeh_server_id;\n",
       "    // Clean up Bokeh references\n",
       "    if (id !== undefined) {\n",
       "      Bokeh.index[id].model.document.clear();\n",
       "      delete Bokeh.index[id];\n",
       "    }\n",
       "\n",
       "    if (server_id !== undefined) {\n",
       "      // Clean up Bokeh references\n",
       "      var cmd = \"from bokeh.io.state import curstate; print(curstate().uuid_to_server['\" + server_id + \"'].get_sessions()[0].document.roots[0]._id)\";\n",
       "      cell.notebook.kernel.execute(cmd, {\n",
       "        iopub: {\n",
       "          output: function(msg) {\n",
       "            var element_id = msg.content.text.trim();\n",
       "            Bokeh.index[element_id].model.document.clear();\n",
       "            delete Bokeh.index[element_id];\n",
       "          }\n",
       "        }\n",
       "      });\n",
       "      // Destroy server and session\n",
       "      var cmd = \"import bokeh.io.notebook as ion; ion.destroy_server('\" + server_id + \"')\";\n",
       "      cell.notebook.kernel.execute(cmd);\n",
       "    }\n",
       "  }\n",
       "\n",
       "  /**\n",
       "   * Handle when a new output is added\n",
       "   */\n",
       "  function handleAddOutput(event, handle) {\n",
       "    var output_area = handle.output_area;\n",
       "    var output = handle.output;\n",
       "\n",
       "    // limit handleAddOutput to display_data with EXEC_MIME_TYPE content only\n",
       "    if ((output.output_type != \"display_data\") || (!output.data.hasOwnProperty(EXEC_MIME_TYPE))) {\n",
       "      return\n",
       "    }\n",
       "\n",
       "    var toinsert = output_area.element.find(\".\" + CLASS_NAME.split(' ')[0]);\n",
       "\n",
       "    if (output.metadata[EXEC_MIME_TYPE][\"id\"] !== undefined) {\n",
       "      toinsert[0].firstChild.textContent = output.data[JS_MIME_TYPE];\n",
       "      // store reference to embed id on output_area\n",
       "      output_area._bokeh_element_id = output.metadata[EXEC_MIME_TYPE][\"id\"];\n",
       "    }\n",
       "    if (output.metadata[EXEC_MIME_TYPE][\"server_id\"] !== undefined) {\n",
       "      var bk_div = document.createElement(\"div\");\n",
       "      bk_div.innerHTML = output.data[HTML_MIME_TYPE];\n",
       "      var script_attrs = bk_div.children[0].attributes;\n",
       "      for (var i = 0; i < script_attrs.length; i++) {\n",
       "        toinsert[0].firstChild.setAttribute(script_attrs[i].name, script_attrs[i].value);\n",
       "      }\n",
       "      // store reference to server id on output_area\n",
       "      output_area._bokeh_server_id = output.metadata[EXEC_MIME_TYPE][\"server_id\"];\n",
       "    }\n",
       "  }\n",
       "\n",
       "  function register_renderer(events, OutputArea) {\n",
       "\n",
       "    function append_mime(data, metadata, element) {\n",
       "      // create a DOM node to render to\n",
       "      var toinsert = this.create_output_subarea(\n",
       "        metadata,\n",
       "        CLASS_NAME,\n",
       "        EXEC_MIME_TYPE\n",
       "      );\n",
       "      this.keyboard_manager.register_events(toinsert);\n",
       "      // Render to node\n",
       "      var props = {data: data, metadata: metadata[EXEC_MIME_TYPE]};\n",
       "      render(props, toinsert[0]);\n",
       "      element.append(toinsert);\n",
       "      return toinsert\n",
       "    }\n",
       "\n",
       "    /* Handle when an output is cleared or removed */\n",
       "    events.on('clear_output.CodeCell', handleClearOutput);\n",
       "    events.on('delete.Cell', handleClearOutput);\n",
       "\n",
       "    /* Handle when a new output is added */\n",
       "    events.on('output_added.OutputArea', handleAddOutput);\n",
       "\n",
       "    /**\n",
       "     * Register the mime type and append_mime function with output_area\n",
       "     */\n",
       "    OutputArea.prototype.register_mime_type(EXEC_MIME_TYPE, append_mime, {\n",
       "      /* Is output safe? */\n",
       "      safe: true,\n",
       "      /* Index of renderer in `output_area.display_order` */\n",
       "      index: 0\n",
       "    });\n",
       "  }\n",
       "\n",
       "  // register the mime type if in Jupyter Notebook environment and previously unregistered\n",
       "  if (root.Jupyter !== undefined) {\n",
       "    var events = require('base/js/events');\n",
       "    var OutputArea = require('notebook/js/outputarea').OutputArea;\n",
       "\n",
       "    if (OutputArea.prototype.mime_types().indexOf(EXEC_MIME_TYPE) == -1) {\n",
       "      register_renderer(events, OutputArea);\n",
       "    }\n",
       "  }\n",
       "\n",
       "  \n",
       "  if (typeof (root._bokeh_timeout) === \"undefined\" || force === true) {\n",
       "    root._bokeh_timeout = Date.now() + 5000;\n",
       "    root._bokeh_failed_load = false;\n",
       "  }\n",
       "\n",
       "  var NB_LOAD_WARNING = {'data': {'text/html':\n",
       "     \"<div style='background-color: #fdd'>\\n\"+\n",
       "     \"<p>\\n\"+\n",
       "     \"BokehJS does not appear to have successfully loaded. If loading BokehJS from CDN, this \\n\"+\n",
       "     \"may be due to a slow or bad network connection. Possible fixes:\\n\"+\n",
       "     \"</p>\\n\"+\n",
       "     \"<ul>\\n\"+\n",
       "     \"<li>re-rerun `output_notebook()` to attempt to load from CDN again, or</li>\\n\"+\n",
       "     \"<li>use INLINE resources instead, as so:</li>\\n\"+\n",
       "     \"</ul>\\n\"+\n",
       "     \"<code>\\n\"+\n",
       "     \"from bokeh.resources import INLINE\\n\"+\n",
       "     \"output_notebook(resources=INLINE)\\n\"+\n",
       "     \"</code>\\n\"+\n",
       "     \"</div>\"}};\n",
       "\n",
       "  function display_loaded() {\n",
       "    var el = document.getElementById(\"914b9e35-4d0e-443a-a9a9-4afc9375ec4c\");\n",
       "    if (el != null) {\n",
       "      el.textContent = \"BokehJS is loading...\";\n",
       "    }\n",
       "    if (root.Bokeh !== undefined) {\n",
       "      if (el != null) {\n",
       "        el.textContent = \"BokehJS \" + root.Bokeh.version + \" successfully loaded.\";\n",
       "      }\n",
       "    } else if (Date.now() < root._bokeh_timeout) {\n",
       "      setTimeout(display_loaded, 100)\n",
       "    }\n",
       "  }\n",
       "\n",
       "\n",
       "  function run_callbacks() {\n",
       "    try {\n",
       "      root._bokeh_onload_callbacks.forEach(function(callback) { callback() });\n",
       "    }\n",
       "    finally {\n",
       "      delete root._bokeh_onload_callbacks\n",
       "    }\n",
       "    console.info(\"Bokeh: all callbacks have finished\");\n",
       "  }\n",
       "\n",
       "  function load_libs(js_urls, callback) {\n",
       "    root._bokeh_onload_callbacks.push(callback);\n",
       "    if (root._bokeh_is_loading > 0) {\n",
       "      console.log(\"Bokeh: BokehJS is being loaded, scheduling callback at\", now());\n",
       "      return null;\n",
       "    }\n",
       "    if (js_urls == null || js_urls.length === 0) {\n",
       "      run_callbacks();\n",
       "      return null;\n",
       "    }\n",
       "    console.log(\"Bokeh: BokehJS not loaded, scheduling load and callback at\", now());\n",
       "    root._bokeh_is_loading = js_urls.length;\n",
       "    for (var i = 0; i < js_urls.length; i++) {\n",
       "      var url = js_urls[i];\n",
       "      var s = document.createElement('script');\n",
       "      s.src = url;\n",
       "      s.async = false;\n",
       "      s.onreadystatechange = s.onload = function() {\n",
       "        root._bokeh_is_loading--;\n",
       "        if (root._bokeh_is_loading === 0) {\n",
       "          console.log(\"Bokeh: all BokehJS libraries loaded\");\n",
       "          run_callbacks()\n",
       "        }\n",
       "      };\n",
       "      s.onerror = function() {\n",
       "        console.warn(\"failed to load library \" + url);\n",
       "      };\n",
       "      console.log(\"Bokeh: injecting script tag for BokehJS library: \", url);\n",
       "      document.getElementsByTagName(\"head\")[0].appendChild(s);\n",
       "    }\n",
       "  };var element = document.getElementById(\"914b9e35-4d0e-443a-a9a9-4afc9375ec4c\");\n",
       "  if (element == null) {\n",
       "    console.log(\"Bokeh: ERROR: autoload.js configured with elementid '914b9e35-4d0e-443a-a9a9-4afc9375ec4c' but no matching script tag was found. \")\n",
       "    return false;\n",
       "  }\n",
       "\n",
       "  var js_urls = [\"https://cdn.pydata.org/bokeh/release/bokeh-0.12.13.min.js\", \"https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.13.min.js\", \"https://cdn.pydata.org/bokeh/release/bokeh-tables-0.12.13.min.js\", \"https://cdn.pydata.org/bokeh/release/bokeh-gl-0.12.13.min.js\"];\n",
       "\n",
       "  var inline_js = [\n",
       "    function(Bokeh) {\n",
       "      Bokeh.set_log_level(\"info\");\n",
       "    },\n",
       "    \n",
       "    function(Bokeh) {\n",
       "      \n",
       "    },\n",
       "    function(Bokeh) {\n",
       "      console.log(\"Bokeh: injecting CSS: https://cdn.pydata.org/bokeh/release/bokeh-0.12.13.min.css\");\n",
       "      Bokeh.embed.inject_css(\"https://cdn.pydata.org/bokeh/release/bokeh-0.12.13.min.css\");\n",
       "      console.log(\"Bokeh: injecting CSS: https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.13.min.css\");\n",
       "      Bokeh.embed.inject_css(\"https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.13.min.css\");\n",
       "      console.log(\"Bokeh: injecting CSS: https://cdn.pydata.org/bokeh/release/bokeh-tables-0.12.13.min.css\");\n",
       "      Bokeh.embed.inject_css(\"https://cdn.pydata.org/bokeh/release/bokeh-tables-0.12.13.min.css\");\n",
       "    }\n",
       "  ];\n",
       "\n",
       "  function run_inline_js() {\n",
       "    \n",
       "    if ((root.Bokeh !== undefined) || (force === true)) {\n",
       "      for (var i = 0; i < inline_js.length; i++) {\n",
       "        inline_js[i].call(root, root.Bokeh);\n",
       "      }if (force === true) {\n",
       "        display_loaded();\n",
       "      }} else if (Date.now() < root._bokeh_timeout) {\n",
       "      setTimeout(run_inline_js, 100);\n",
       "    } else if (!root._bokeh_failed_load) {\n",
       "      console.log(\"Bokeh: BokehJS failed to load within specified timeout.\");\n",
       "      root._bokeh_failed_load = true;\n",
       "    } else if (force !== true) {\n",
       "      var cell = $(document.getElementById(\"914b9e35-4d0e-443a-a9a9-4afc9375ec4c\")).parents('.cell').data().cell;\n",
       "      cell.output_area.append_execute_result(NB_LOAD_WARNING)\n",
       "    }\n",
       "\n",
       "  }\n",
       "\n",
       "  if (root._bokeh_is_loading === 0) {\n",
       "    console.log(\"Bokeh: BokehJS loaded, going straight to plotting\");\n",
       "    run_inline_js();\n",
       "  } else {\n",
       "    load_libs(js_urls, function() {\n",
       "      console.log(\"Bokeh: BokehJS plotting callback run at\", now());\n",
       "      run_inline_js();\n",
       "    });\n",
       "  }\n",
       "}(window));"
      ],
      "application/vnd.bokehjs_load.v0+json": "\n(function(root) {\n  function now() {\n    return new Date();\n  }\n\n  var force = true;\n\n  if (typeof (root._bokeh_onload_callbacks) === \"undefined\" || force === true) {\n    root._bokeh_onload_callbacks = [];\n    root._bokeh_is_loading = undefined;\n  }\n\n  \n\n  \n  if (typeof (root._bokeh_timeout) === \"undefined\" || force === true) {\n    root._bokeh_timeout = Date.now() + 5000;\n    root._bokeh_failed_load = false;\n  }\n\n  var NB_LOAD_WARNING = {'data': {'text/html':\n     \"<div style='background-color: #fdd'>\\n\"+\n     \"<p>\\n\"+\n     \"BokehJS does not appear to have successfully loaded. If loading BokehJS from CDN, this \\n\"+\n     \"may be due to a slow or bad network connection. Possible fixes:\\n\"+\n     \"</p>\\n\"+\n     \"<ul>\\n\"+\n     \"<li>re-rerun `output_notebook()` to attempt to load from CDN again, or</li>\\n\"+\n     \"<li>use INLINE resources instead, as so:</li>\\n\"+\n     \"</ul>\\n\"+\n     \"<code>\\n\"+\n     \"from bokeh.resources import INLINE\\n\"+\n     \"output_notebook(resources=INLINE)\\n\"+\n     \"</code>\\n\"+\n     \"</div>\"}};\n\n  function display_loaded() {\n    var el = document.getElementById(\"914b9e35-4d0e-443a-a9a9-4afc9375ec4c\");\n    if (el != null) {\n      el.textContent = \"BokehJS is loading...\";\n    }\n    if (root.Bokeh !== undefined) {\n      if (el != null) {\n        el.textContent = \"BokehJS \" + root.Bokeh.version + \" successfully loaded.\";\n      }\n    } else if (Date.now() < root._bokeh_timeout) {\n      setTimeout(display_loaded, 100)\n    }\n  }\n\n\n  function run_callbacks() {\n    try {\n      root._bokeh_onload_callbacks.forEach(function(callback) { callback() });\n    }\n    finally {\n      delete root._bokeh_onload_callbacks\n    }\n    console.info(\"Bokeh: all callbacks have finished\");\n  }\n\n  function load_libs(js_urls, callback) {\n    root._bokeh_onload_callbacks.push(callback);\n    if (root._bokeh_is_loading > 0) {\n      console.log(\"Bokeh: BokehJS is being loaded, scheduling callback at\", now());\n      return null;\n    }\n    if (js_urls == null || js_urls.length === 0) {\n      run_callbacks();\n      return null;\n    }\n    console.log(\"Bokeh: BokehJS not loaded, scheduling load and callback at\", now());\n    root._bokeh_is_loading = js_urls.length;\n    for (var i = 0; i < js_urls.length; i++) {\n      var url = js_urls[i];\n      var s = document.createElement('script');\n      s.src = url;\n      s.async = false;\n      s.onreadystatechange = s.onload = function() {\n        root._bokeh_is_loading--;\n        if (root._bokeh_is_loading === 0) {\n          console.log(\"Bokeh: all BokehJS libraries loaded\");\n          run_callbacks()\n        }\n      };\n      s.onerror = function() {\n        console.warn(\"failed to load library \" + url);\n      };\n      console.log(\"Bokeh: injecting script tag for BokehJS library: \", url);\n      document.getElementsByTagName(\"head\")[0].appendChild(s);\n    }\n  };var element = document.getElementById(\"914b9e35-4d0e-443a-a9a9-4afc9375ec4c\");\n  if (element == null) {\n    console.log(\"Bokeh: ERROR: autoload.js configured with elementid '914b9e35-4d0e-443a-a9a9-4afc9375ec4c' but no matching script tag was found. \")\n    return false;\n  }\n\n  var js_urls = [\"https://cdn.pydata.org/bokeh/release/bokeh-0.12.13.min.js\", \"https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.13.min.js\", \"https://cdn.pydata.org/bokeh/release/bokeh-tables-0.12.13.min.js\", \"https://cdn.pydata.org/bokeh/release/bokeh-gl-0.12.13.min.js\"];\n\n  var inline_js = [\n    function(Bokeh) {\n      Bokeh.set_log_level(\"info\");\n    },\n    \n    function(Bokeh) {\n      \n    },\n    function(Bokeh) {\n      console.log(\"Bokeh: injecting CSS: https://cdn.pydata.org/bokeh/release/bokeh-0.12.13.min.css\");\n      Bokeh.embed.inject_css(\"https://cdn.pydata.org/bokeh/release/bokeh-0.12.13.min.css\");\n      console.log(\"Bokeh: injecting CSS: https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.13.min.css\");\n      Bokeh.embed.inject_css(\"https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.13.min.css\");\n      console.log(\"Bokeh: injecting CSS: https://cdn.pydata.org/bokeh/release/bokeh-tables-0.12.13.min.css\");\n      Bokeh.embed.inject_css(\"https://cdn.pydata.org/bokeh/release/bokeh-tables-0.12.13.min.css\");\n    }\n  ];\n\n  function run_inline_js() {\n    \n    if ((root.Bokeh !== undefined) || (force === true)) {\n      for (var i = 0; i < inline_js.length; i++) {\n        inline_js[i].call(root, root.Bokeh);\n      }if (force === true) {\n        display_loaded();\n      }} else if (Date.now() < root._bokeh_timeout) {\n      setTimeout(run_inline_js, 100);\n    } else if (!root._bokeh_failed_load) {\n      console.log(\"Bokeh: BokehJS failed to load within specified timeout.\");\n      root._bokeh_failed_load = true;\n    } else if (force !== true) {\n      var cell = $(document.getElementById(\"914b9e35-4d0e-443a-a9a9-4afc9375ec4c\")).parents('.cell').data().cell;\n      cell.output_area.append_execute_result(NB_LOAD_WARNING)\n    }\n\n  }\n\n  if (root._bokeh_is_loading === 0) {\n    console.log(\"Bokeh: BokehJS loaded, going straight to plotting\");\n    run_inline_js();\n  } else {\n    load_libs(js_urls, function() {\n      console.log(\"Bokeh: BokehJS plotting callback run at\", now());\n      run_inline_js();\n    });\n  }\n}(window));"
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "\n",
       "<div class=\"bk-root\">\n",
       "    <div class=\"bk-plotdiv\" id=\"3b813def-9451-4820-9910-9b05851bd601\"></div>\n",
       "</div>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/javascript": [
       "(function(root) {\n",
       "  function embed_document(root) {\n",
       "    \n",
       "  var docs_json = {\"eac557b3-5a68-4f63-a7e3-891e03369f82\":{\"roots\":{\"references\":[{\"attributes\":{\"callback\":null,\"column_names\":[\"x\",\"top\"],\"data\":{\"top\":[2,0,5,4,5],\"x\":[\"8.97\",\"12.38\",\"15.78\",\"19.19\",\"22.59\"]}},\"id\":\"6f431d23-33cb-4a95-a7ab-a2406688e485\",\"type\":\"ColumnDataSource\"},{\"attributes\":{},\"id\":\"a0a56296-80bc-46aa-bc03-f251edb9af11\",\"type\":\"BasicTickFormatter\"},{\"attributes\":{\"bottom_units\":\"screen\",\"fill_alpha\":{\"value\":0.5},\"fill_color\":{\"value\":\"lightgrey\"},\"left_units\":\"screen\",\"level\":\"overlay\",\"line_alpha\":{\"value\":1.0},\"line_color\":{\"value\":\"black\"},\"line_dash\":[4,4],\"line_width\":{\"value\":2},\"plot\":null,\"render_mode\":\"css\",\"right_units\":\"screen\",\"top_units\":\"screen\"},\"id\":\"ff13b0b3-299c-4779-b71b-94adc12b876b\",\"type\":\"BoxAnnotation\"},{\"attributes\":{},\"id\":\"1957e139-ef73-4c75-b325-ea9f2724ee86\",\"type\":\"CategoricalScale\"},{\"attributes\":{},\"id\":\"93d5aa5a-e91e-44cc-b9b1-3a750cd09621\",\"type\":\"PanTool\"},{\"attributes\":{\"plot\":null,\"text\":\"Histogram of fuel economy\"},\"id\":\"ea0b6514-205e-4192-ae33-282d0d43b4ff\",\"type\":\"Title\"},{\"attributes\":{},\"id\":\"d5f8ca6c-a885-4845-8808-4618ea366790\",\"type\":\"WheelZoomTool\"},{\"attributes\":{\"callback\":null,\"factors\":[\"8.97\",\"12.38\",\"15.78\",\"19.19\",\"22.59\"]},\"id\":\"4fe9be34-9ecb-41a2-a7a2-f51c9ba80fed\",\"type\":\"FactorRange\"},{\"attributes\":{\"overlay\":{\"id\":\"ff13b0b3-299c-4779-b71b-94adc12b876b\",\"type\":\"BoxAnnotation\"}},\"id\":\"e11e4cd2-c4ea-4a74-bbba-b715e567d80e\",\"type\":\"BoxZoomTool\"},{\"attributes\":{\"active_drag\":\"auto\",\"active_inspect\":\"auto\",\"active_scroll\":\"auto\",\"active_tap\":\"auto\",\"tools\":[{\"id\":\"93d5aa5a-e91e-44cc-b9b1-3a750cd09621\",\"type\":\"PanTool\"},{\"id\":\"d5f8ca6c-a885-4845-8808-4618ea366790\",\"type\":\"WheelZoomTool\"},{\"id\":\"e11e4cd2-c4ea-4a74-bbba-b715e567d80e\",\"type\":\"BoxZoomTool\"},{\"id\":\"0c691035-4ee4-4bd3-811d-d7e93e0fce03\",\"type\":\"SaveTool\"},{\"id\":\"89b194d8-b562-4f0f-9014-19d8925c9a52\",\"type\":\"ResetTool\"},{\"id\":\"72d84a49-3115-4c5a-b1ab-d6c8540f87b4\",\"type\":\"HelpTool\"}]},\"id\":\"9f9976b1-37a9-4ff0-9a7d-8f6b0006d759\",\"type\":\"Toolbar\"},{\"attributes\":{},\"id\":\"0c691035-4ee4-4bd3-811d-d7e93e0fce03\",\"type\":\"SaveTool\"},{\"attributes\":{},\"id\":\"5fa43644-b939-4a0b-b904-ab9d9e336ce1\",\"type\":\"CategoricalTicker\"},{\"attributes\":{},\"id\":\"89b194d8-b562-4f0f-9014-19d8925c9a52\",\"type\":\"ResetTool\"},{\"attributes\":{},\"id\":\"ea17921b-13bb-4880-85a5-13ec41a39842\",\"type\":\"LinearScale\"},{\"attributes\":{\"fill_color\":{\"value\":\"#1f77b4\"},\"line_color\":{\"value\":\"#1f77b4\"},\"top\":{\"field\":\"top\"},\"width\":{\"value\":0.9},\"x\":{\"field\":\"x\"}},\"id\":\"22abba24-7f34-4bf9-904a-a2c14b354951\",\"type\":\"VBar\"},{\"attributes\":{},\"id\":\"72d84a49-3115-4c5a-b1ab-d6c8540f87b4\",\"type\":\"HelpTool\"},{\"attributes\":{\"below\":[{\"id\":\"4e85839c-afa2-4296-b2ca-3a23644e183f\",\"type\":\"CategoricalAxis\"}],\"left\":[{\"id\":\"a4c475b2-c914-477b-aa60-bb07bf779bdd\",\"type\":\"LinearAxis\"}],\"plot_height\":350,\"renderers\":[{\"id\":\"4e85839c-afa2-4296-b2ca-3a23644e183f\",\"type\":\"CategoricalAxis\"},{\"id\":\"16a66caf-8dfb-4984-b0e2-0c24c012de8f\",\"type\":\"Grid\"},{\"id\":\"a4c475b2-c914-477b-aa60-bb07bf779bdd\",\"type\":\"LinearAxis\"},{\"id\":\"0b072016-a138-4e61-a3b4-1c27ec62c0a3\",\"type\":\"Grid\"},{\"id\":\"ff13b0b3-299c-4779-b71b-94adc12b876b\",\"type\":\"BoxAnnotation\"},{\"id\":\"c7d5f894-410e-48a6-be7c-ceceefc92de2\",\"type\":\"GlyphRenderer\"}],\"title\":{\"id\":\"ea0b6514-205e-4192-ae33-282d0d43b4ff\",\"type\":\"Title\"},\"toolbar\":{\"id\":\"9f9976b1-37a9-4ff0-9a7d-8f6b0006d759\",\"type\":\"Toolbar\"},\"x_range\":{\"id\":\"4fe9be34-9ecb-41a2-a7a2-f51c9ba80fed\",\"type\":\"FactorRange\"},\"x_scale\":{\"id\":\"1957e139-ef73-4c75-b325-ea9f2724ee86\",\"type\":\"CategoricalScale\"},\"y_range\":{\"id\":\"18a16ef5-89ec-4bd7-8e49-7a8434425df1\",\"type\":\"DataRange1d\"},\"y_scale\":{\"id\":\"ea17921b-13bb-4880-85a5-13ec41a39842\",\"type\":\"LinearScale\"}},\"id\":\"1bbf9cad-ac46-45b6-9cfc-d2cc8651289e\",\"subtype\":\"Figure\",\"type\":\"Plot\"},{\"attributes\":{\"callback\":null},\"id\":\"18a16ef5-89ec-4bd7-8e49-7a8434425df1\",\"type\":\"DataRange1d\"},{\"attributes\":{},\"id\":\"1cc1b034-abfb-424c-bc66-0bf608ef90d1\",\"type\":\"CategoricalTickFormatter\"},{\"attributes\":{\"plot\":{\"id\":\"1bbf9cad-ac46-45b6-9cfc-d2cc8651289e\",\"subtype\":\"Figure\",\"type\":\"Plot\"},\"ticker\":{\"id\":\"5fa43644-b939-4a0b-b904-ab9d9e336ce1\",\"type\":\"CategoricalTicker\"}},\"id\":\"16a66caf-8dfb-4984-b0e2-0c24c012de8f\",\"type\":\"Grid\"},{\"attributes\":{\"data_source\":{\"id\":\"6f431d23-33cb-4a95-a7ab-a2406688e485\",\"type\":\"ColumnDataSource\"},\"glyph\":{\"id\":\"22abba24-7f34-4bf9-904a-a2c14b354951\",\"type\":\"VBar\"},\"hover_glyph\":null,\"muted_glyph\":null,\"nonselection_glyph\":{\"id\":\"5b6da583-e40b-4aba-b814-d2abae38e100\",\"type\":\"VBar\"},\"selection_glyph\":null,\"view\":{\"id\":\"874f3fa9-78f8-4fda-99aa-18b8b00f7caa\",\"type\":\"CDSView\"}},\"id\":\"c7d5f894-410e-48a6-be7c-ceceefc92de2\",\"type\":\"GlyphRenderer\"},{\"attributes\":{\"source\":{\"id\":\"6f431d23-33cb-4a95-a7ab-a2406688e485\",\"type\":\"ColumnDataSource\"}},\"id\":\"874f3fa9-78f8-4fda-99aa-18b8b00f7caa\",\"type\":\"CDSView\"},{\"attributes\":{\"formatter\":{\"id\":\"1cc1b034-abfb-424c-bc66-0bf608ef90d1\",\"type\":\"CategoricalTickFormatter\"},\"plot\":{\"id\":\"1bbf9cad-ac46-45b6-9cfc-d2cc8651289e\",\"subtype\":\"Figure\",\"type\":\"Plot\"},\"ticker\":{\"id\":\"5fa43644-b939-4a0b-b904-ab9d9e336ce1\",\"type\":\"CategoricalTicker\"}},\"id\":\"4e85839c-afa2-4296-b2ca-3a23644e183f\",\"type\":\"CategoricalAxis\"},{\"attributes\":{\"formatter\":{\"id\":\"a0a56296-80bc-46aa-bc03-f251edb9af11\",\"type\":\"BasicTickFormatter\"},\"plot\":{\"id\":\"1bbf9cad-ac46-45b6-9cfc-d2cc8651289e\",\"subtype\":\"Figure\",\"type\":\"Plot\"},\"ticker\":{\"id\":\"8b3eebad-6adc-4ee7-80d9-9daf367444b9\",\"type\":\"BasicTicker\"}},\"id\":\"a4c475b2-c914-477b-aa60-bb07bf779bdd\",\"type\":\"LinearAxis\"},{\"attributes\":{},\"id\":\"8b3eebad-6adc-4ee7-80d9-9daf367444b9\",\"type\":\"BasicTicker\"},{\"attributes\":{\"dimension\":1,\"plot\":{\"id\":\"1bbf9cad-ac46-45b6-9cfc-d2cc8651289e\",\"subtype\":\"Figure\",\"type\":\"Plot\"},\"ticker\":{\"id\":\"8b3eebad-6adc-4ee7-80d9-9daf367444b9\",\"type\":\"BasicTicker\"}},\"id\":\"0b072016-a138-4e61-a3b4-1c27ec62c0a3\",\"type\":\"Grid\"},{\"attributes\":{\"fill_alpha\":{\"value\":0.1},\"fill_color\":{\"value\":\"#1f77b4\"},\"line_alpha\":{\"value\":0.1},\"line_color\":{\"value\":\"#1f77b4\"},\"top\":{\"field\":\"top\"},\"width\":{\"value\":0.9},\"x\":{\"field\":\"x\"}},\"id\":\"5b6da583-e40b-4aba-b814-d2abae38e100\",\"type\":\"VBar\"}],\"root_ids\":[\"1bbf9cad-ac46-45b6-9cfc-d2cc8651289e\"]},\"title\":\"Bokeh Application\",\"version\":\"0.12.13\"}};\n",
       "  var render_items = [{\"docid\":\"eac557b3-5a68-4f63-a7e3-891e03369f82\",\"elementid\":\"3b813def-9451-4820-9910-9b05851bd601\",\"modelid\":\"1bbf9cad-ac46-45b6-9cfc-d2cc8651289e\"}];\n",
       "  root.Bokeh.embed.embed_items_notebook(docs_json, render_items);\n",
       "\n",
       "  }\n",
       "  if (root.Bokeh !== undefined) {\n",
       "    embed_document(root);\n",
       "  } else {\n",
       "    var attempts = 0;\n",
       "    var timer = setInterval(function(root) {\n",
       "      if (root.Bokeh !== undefined) {\n",
       "        embed_document(root);\n",
       "        clearInterval(timer);\n",
       "      }\n",
       "      attempts++;\n",
       "      if (attempts > 100) {\n",
       "        console.log(\"Bokeh: ERROR: Unable to run BokehJS code because BokehJS library is missing\")\n",
       "        clearInterval(timer);\n",
       "      }\n",
       "    }, 10, root)\n",
       "  }\n",
       "})(window);"
      ],
      "application/vnd.bokehjs_exec.v0+json": ""
     },
     "metadata": {
      "application/vnd.bokehjs_exec.v0+json": {
       "id": "1bbf9cad-ac46-45b6-9cfc-d2cc8651289e"
      }
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "%%local\n",
    "from bokeh.io import show\n",
    "from bokeh.plotting import figure\n",
    "from bokeh.io import output_notebook\n",
    "output_notebook()\n",
    "\n",
    "labels = [str(round(e, 2)) for e in hist_MPG['bins']]\n",
    "\n",
    "p = figure(\n",
    "    x_range=labels, \n",
    "    plot_height=350, \n",
    "    title='Histogram of fuel economy'\n",
    ")\n",
    "\n",
    "p.vbar(x=labels, top=hist_MPG['counts'], width=0.9)\n",
    "\n",
    "show(p)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Visualizing interactions between features"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {},
   "outputs": [],
   "source": [
    "scatter = (\n",
    "    no_outliers\n",
    "    .select('Displacement', 'Cylinders')\n",
    ")\n",
    "\n",
    "scatter.registerTempTable('scatter')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%sql -o scatter_source -q\n",
    "SELECT * FROM scatter"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Text(0.5,1,'Relationship between cylinders and displacement')"
      ]
     },
     "execution_count": 38,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAtcAAAIqCAYAAADvgmaaAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMS4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvNQv5yAAAIABJREFUeJzs3XlgVOW9//HPyUy2gRDJwpIEUEDEIBoKaEAKaYhi3WrFtuIGcakVXNB6K3WLbV2QalXA2ipXcK3oT8R6lYpBEKVEDZgiBMMqGgMmIexZZ/L8/shlrkMyZELOzBB4v/6aOec5Z74z50ny4fCdcyxjjBEAAACAdosIdwEAAADAsYJwDQAAANiEcA0AAADYhHANAAAA2IRwDQAAANiEcA0AAADYhHANhEFWVpauv/76du9n2bJlsixLpaWlNlQVuEDqf+CBB9S/f/92v9aJJ56oBx98sN37gTRp0iTl5OR4n9t1jL7++mtZlqVPPvmk3fs6mhzp+5o3b56cTqff53Y49FgCOHoQroEATZo0SZZlybIsORwOpaWl6ZprrtF3330Xktd3Op2aN2+ez7KRI0dq+/btSklJCUkNbXHnnXeqoKAg3GV4tfT5He+OtmN0rPrVr34Vst8Tx7JPPvlElmXp66+/DncpwGERroE2+PGPf6zt27frm2++0auvvqovvvhCv/jFL8JWT1RUlHr06KGIiKPvR7lz585KSkoKdxk4jKPpGBlj1NDQEO4ygiI2Nlbdu3cPdxkAQuTo+4sMHMUOhtnU1FSNHj1av/71r7Vy5Urt3bvXZ9ysWbM0cOBAxcTE6OSTT9ZDDz0kt9vtd78ffPCBsrKylJCQoPj4eI0ZM0afffaZd/2JJ54oj8ej3Nxc79lzqeW2kIKCAo0ePVqxsbHq2rWrrrjiCpWXl3vXH2wFePvttzVw4EB16tRJP/nJT7R582bvmL179yo3N1c9evRQdHS0evXqpTvuuKNZ3X/605/Uo0cPJSQkaNKkSTpw4ECz1zn0+auvvqq+ffsqJiZGOTk52rp1a6ufe01Nja6//np16dJFSUlJuuuuu9TY2Ohd73a79cADD+ikk05STEyMBg0apL///e+tfn69evXSnDlzvOMmTpwoy7K0adMm77I+ffror3/9q/f5a6+9poyMDMXExOjEE0/UHXfc4fO+pdaP/4knnqj7779ft912mxISEtS9e3fdeeed8ng8h/0cysvLlZubq+7duysmJkannHKKnn/+eTU2Nqpv3756+OGHfcYfOHBAXbp08XvG3t8xOtzckKTXX39d/fv3V0xMjEaOHKk1a9Y02/emTZs0fvx4nXDCCeratavOPfdcffnll971B1slli5dqiFDhig6Olrvv/++SktLNX78eCUlJSk2NlZ9+/bVn//8Z7+fiTFGN9xwg/r16+cdf/fdd6uuri4o76ul17/vvvvUrVs3de7cWZdffrl27drlM+bQtpDWfr6ysrJ07bXXatq0aUpKSlKXLl10/fXXq6amxm8dq1ev1k9/+lNvHcOHD9e//vUvnzFut1t//OMf1a9fP0VHRys1NVW33HKLd/3+/ft12223KTU1VS6XS0OGDNGCBQu86w+2ybz66qsaN26cXC6XBg4cqI8++kjfffedzj//fHXq1Enp6en6+OOPfV470PmwYsUK/ehHP5LL5dLw4cO1atUq72v/+Mc/liSddNJJsixLWVlZrR0eIDwMgIBMnDjRjB071vv8u+++M6NHjzYOh8Ps37/fuzwvL8/07t3bLFiwwGzZssW8++67plevXubee+/1jhkzZoy57rrrvM8XLFhgXn/9dVNSUmLWrl1rrrvuOtO1a1dTWVlpjDGmvLzcOBwO8+STT5rt27eb7du3G2OMWbp0qZFkvv32W2OMMdu3bzdxcXFmwoQJZs2aNebjjz82gwcPNqNGjfKpz+VymXHjxpnCwkJTVFRkMjIyzOjRo71jbrnlFnP66aebgoICs23bNrNixQrz7LPP+tQfHx9vpk6datavX28WLVpk4uPjzf333+/zOv369Wv2umeffbb57LPPzGeffWbOPPNMc/rpp5vGxka/n3ufPn1MXFycue+++8xXX31lXnzxReNyuczjjz/uc2wGDx5s3n//fbNlyxbz2muvmfj4eDNnzpzDfn5XX321ufzyy7376dWrl0lOTjZ/+9vfjDHGbNq0yUgy69evN8YYM3fuXHPCCSeYF1980WzevNl89NFHZvDgweaqq65q0/Hv06ePOeGEE8wjjzxiNmzYYF577TXjcDjM888/7/dzqK6uNgMHDjRDhgwxH3zwgdm8ebN5//33zT/+8Q9jjDEPP/yw6du3r89nOWfOHBMfH28OHDjg/Zx+OIf9HaPDzY3Vq1cby7LMtGnTzFdffWXefPNNc+KJJxpJ5uOPPzbGGLNjxw7TvXt385vf/MasWbPGfPXVV+bmm282CQkJpry83PtZWpZlhg0bZpYsWWI2b95sysvLzUUXXWTGjh1rvvjiC7N161bz4YcfmldffdXv5+LxeMw999xjCgoKzNatW83bb79tevTo0Wwu2vG+WvLkk08al8tl5s2bZ0pKSsyjjz5q4uPjjcPh8I6ZO3euz/NAfr7i4uLM9ddfb4qLi80///lPk5ycbG655RbvmEOP5dKlS828efPMunXrTElJibnnnntMZGSkKSkp8Y655pprTHJysnnxxRfNpk2bzMqVK81f/vIXY4wxjY2NJisry4wZM8Z8/PHHZvPmzebvf/+7iYyMNPn5+cYYY7Zu3Wokmb59+5q33nrLlJSUmEsuucT07NnTjB071ixYsMCUlJSYSy+91KSlpZn6+vo2z4cf//jHZvny5Wb9+vXmnHPOMX379jUNDQ3G7Xabt99+20gyn332mdm+fbvZuXOn3+MChBPhGgjQxIkTjcPhMJ06dTKxsbFGkpFkfvvb33rHHDhwwMTGxppFixb5bPvCCy+Y+Ph47/NDw/WhPB6POeGEE8zLL7/sXeZwOMzcuXN9xh0aru+9916Tmppq6urqvGOKioqMJPPRRx8ZY5qChsPh8P5RM8aYf/zjH8ayLFNTU2OMMebiiy82EydO9FvfmDFjzODBg32W3XjjjSYzM9P7vKXgJsls3LjRu6ykpMRIMh988IHf1+rTp4/PPw6MMeb3v/+9SU1NNcYYs2XLFmNZljcAH/SHP/zBnHHGGd7nLX1+c+fONd26dTPGGLNhwwYTGxtr/vjHP5pf/OIXxhhjnn32WdOzZ0+fWp555hmffXz00UdGkqmqqgr4+Pfp08dcdNFFPmPGjRvnE/QPNWfOHBMdHe091ofasWOHiYyM9PksMzMzzeTJk73PAwnXrc2NK6+80owYMcLntWfNmuUTQvPy8sxZZ53lM6axsdH07dvXPPHEE8aYps9eklm+fLnPuNNPP93k5eX5/RwC8Ze//MX079/f9vfVktTUVHP33Xf7LBs/fvxhw3UgP199+vQxbrfbu+zvf/+7iYqK8v5D/tBj2ZLTTz/dPPjgg8YYYzZu3GgkmTfeeKPFsUuXLjXR0dFm9+7dPstzc3PNz372M2PM/4Xrg8fQGGM+++wzI8k89thj3mWrV682ksyXX35pjGnbfFi1apV3zMqVK40k89VXXxljjPn444+NJLN169bDvm8g3Oz9+jJwjDvrrLP0wgsvqLa2Vq+//ro++OAD/elPf/KuX7dunWpqajR+/Hhv64EkeTwe1dbWqqKiQsnJyc32u3XrVt1///1auXKlysvL1djYqOrqam3btq1N9a1bt06ZmZmKioryLjvjjDMUHx+vdevWafTo0ZKklJQUnzpSU1NljFF5ebl69+6tyZMna/z48SosLNTYsWN13nnnady4cT693RkZGT6vnZqaqsWLFx+2vuTkZJ82hAEDBigpKUnFxcWHvfLBiBEjfJ6fffbZeuSRR7R3714VFhbKGKNhw4b5jHG73XI4HIetZ+zYsSovL9fatWu1YsUKjRo1Suedd55mzpwpY4w+/PBDZWdnS5IqKiq0bds23XHHHbrzzju9+zDGSJK3lSTQ49/S53e4FplVq1YpPT1daWlpLa7v3r27fvazn+m5555TTk6O1q1bp4KCAj3zzDOH/QwO1drcKC4u1tixY322GTVqlM/zzz//XKtWrVLnzp19ltfU1Gjjxo0+y4YPH+7zfOrUqbrxxhu1aNEiZWVl6YILLvDOW3+ee+45zZkzR19//bUOHDggt9vt0zZk1/s61N69e/Xdd99p5MiRzbZbuHCh3+0C+fk688wzfebv2Wefrfr6em3evFmnn356s31WVFQoLy9PH374oXbs2CG3263a2lrv75DVq1dLks4999wWa/r8889VX1+v1NRUn+X19fU6+eSTfZadccYZ3sc9evSQJJ+aDi472I4W6HywLMtn3wdr+f7773XKKae0WDdwNCJcA20QGxvrDYennXaaNmzYoClTpuj555+XJO8f9DfeeEMDBgxotn1CQkKL+73wwguVlJSkp59+Wr169VJUVJRGjRql+vr6Ntf4w1Dnb/kPw/cP1x2sf9y4cfrmm2/0/vvva9myZbrqqqs0ePBgLVmyxPsHv6V9HBpoAnEwnB7pNgdf89///rdcLlezmg6nV69e6tevn5YsWaJ///vfys7O1tChQ+V2u7VmzRotXbrU28d88HWeeuop/eQnP2m2r7S0NP3nP/+RFNjxP5LPr7X385vf/Ebnn3++Kioq9Nxzz2n48OHNQnxrWpsbxphW62hsbNTYsWM1e/bsZuvi4+O9jx0Oh2JiYnzW5+bm6rzzztO//vUvLV26VD/96U/185//XC+//HKLr/XGG29oypQpmj59usaMGaMuXbrojTfe0D333GP7+zrUwXnY1u0C+fny91r+TJo0Sd98841mzJihk046SbGxsbr88ssD/h3S2Nio+Ph4ff75583WHfrZRUZGeh8ffO8tLTv42QY6HyIiInze/6H7AToKwjXQDg888IAGDRqkyZMna9iwYRo0aJBiYmK0ZcsWnX/++QHtY+fOnSouLtZ7772ncePGSZJKS0t9voQoNf2Ba+0Lb4MGDdLcuXNVX1/v/YP4n//8R3v27NGgQYPa9N4SEhI0YcIETZgwQbm5uRoxYoSKi4s1ePDgNu3nhyoqKrR582b169dPkrRhwwbt3LlTp5566mG3O/RycStXrlRKSoq6dOmioUOHSpK++eYbXXjhhX734e/zy87O1pIlS/Tpp5/qzjvvVEREhEaPHq1Zs2bp+++/95657t69u3r16qWSkhLdcMMNLb7GkRz/QA0dOlTPP/+8SktL/Z69zs7OVu/evfXss8/qpZde0vTp022tQWp6jytWrPBZdujzYcOGad68eUpNTVVsbGybX6Nnz57Kzc1Vbm6uzj//fE2YMEF//etf1aVLl2Zjly9friFDhvh8IfBILtUWyPs6VHx8vFJTU7VixQqf493adlLrP1+ff/65PB6PN2yuXLlSUVFR3p+dQy1fvlwzZszQxRdfLKnpy6xbtmzRaaedJkn60Y9+JElavHixLrvssmbbDxs2TLt371Ztba13G7u0dz4cdPB3Wmu/B4Fw42ohQDsMHDhQF154oX7/+99Larq02d133627775bs2fPVklJidatW6fXXntNd911V4v76Nq1q5KTk/Xcc89pw4YNWrlypSZMmNDsj9BJJ52kpUuXqqysTJWVlS3u6+abb9bevXs1adIkrV27Vp988omuvvpqjRo1yvtN+0Dcc889WrBggUpKSrRx40a98sor6ty5s3r37h3wPlricrmUm5urVatWqbCwUBMnTtTgwYNbvRlGUVGRHnjgAW3YsEGvvvqqnnrqKd1+++2SpP79++vaa6/VDTfcoJdeekmbNm3Sf/7zHz3//PN69NFHvfvw9/llZ2dr0aJFqqur8waQ7OxsvfDCCzrppJN04oknesc+9NBDmjlzph588EGtXbtWJSUlWrhwoW688UZJR3b8AzVhwgT16dNHF198sfLz87V161YtWbJE8+fP946xLEu//vWv9cc//lH19fWaMGFCu16zJbfffrtWrlype+65Rxs2bNBbb72lxx9/3GfMzTffLI/Ho0suuUQff/yxvv76a33yySe655579O9///uw+7/55pv13nvvafPmzVq3bp0WLFigXr16KS4ursXxp5xyir788ku9/fbb2rx5s5566imfK1zY+b5a8tvf/lZPPfWUXnrpJW3cuFGPP/648vPzD7tNID9fO3fu1JQpU7R+/Xq9++67uu+++3TDDTeoU6dOLe7zlFNO0SuvvKIvv/xSRUVFmjBhgk8I7d+/v6688kpNnjxZL7/8sjZv3qzPP/9cTz31lKSmOZ+Tk6NLL71Ub731lrZs2aJVq1Zp1qxZeu655wL5CP1qz3z4oT59+igiIkLvvfeeysvLtWfPnnbVBQQL4Rpop9/97nfKz8/XkiVLJEn33XefnnjiCc2ZM0dnnHGGRo0apSeeeMInpP1QRESE3njjDW8v5aRJkzR16lT17NnTZ9zjjz+uVatW6aSTTmqxb1tqOru6ePFilZaWavjw4brwwgt12mmn6c0332zTe4qJidH999+voUOHatiwYVqzZo0WLVrk81+4R6Jnz5769a9/rfHjx+vss89WbGys3nrrrVb/W/2WW27Rtm3bNGzYMN1888266aabvOFakp599lndfvvteuihh5Senq6xY8fqhRdeUN++fb1j/H1+2dnZ8ng8GjNmjPcsYXZ2ttxut/es9UFXX321Xn/9db377rs688wzNXz4cD3wwAM+faptPf6Bcrlc+uijj3Taaafp8ssv16mnnqopU6Y0uzxbbm6ujDG64oormvW42mHo0KF69dVX9dprr2nw4MGaPn26nnjiCZ8x3bt318qVK5WUlKRLL71Up5xyiq688kpt27at2bw+lDFGU6dO1WmnnabRo0frwIEDWrRokd85cuONN+rqq69Wbm6uhgwZok8//VQPPPBAUN5XS2677Tbdeuutuv3225WRkaGVK1fq/vvvP+w2gfx8XXbZZYqLi9OoUaN0+eWX6/zzz9eMGTP87nPu3LlqbGzUmWeeqUsuuUTnnXdes372uXPn6sYbb9S9996rU089VT//+c+9ff6WZemf//ynLr30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\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x113242da0>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "%%local\n",
    "%matplotlib inline\n",
    "import matplotlib.pyplot as plt\n",
    "plt.style.use('ggplot')\n",
    "\n",
    "fig = plt.figure(figsize=(12,9))\n",
    "ax = fig.add_subplot(1, 1, 1)\n",
    "ax.scatter(\n",
    "      list(scatter_source['Cylinders'])\n",
    "    , list(scatter_source['Displacement'])\n",
    "    , s = 200\n",
    "    , alpha = 0.5\n",
    ")\n",
    "\n",
    "ax.set_xlabel('Cylinders')\n",
    "ax.set_ylabel('Displacement')\n",
    "\n",
    "ax.set_title('Relationship between cylinders and displacement')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "\n",
       "    <div class=\"bk-root\">\n",
       "        <a href=\"https://bokeh.pydata.org\" target=\"_blank\" class=\"bk-logo bk-logo-small bk-logo-notebook\"></a>\n",
       "        <span id=\"536c3786-9b17-48e4-bdf7-84f09eee2705\">Loading BokehJS ...</span>\n",
       "    </div>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/javascript": [
       "\n",
       "(function(root) {\n",
       "  function now() {\n",
       "    return new Date();\n",
       "  }\n",
       "\n",
       "  var force = true;\n",
       "\n",
       "  if (typeof (root._bokeh_onload_callbacks) === \"undefined\" || force === true) {\n",
       "    root._bokeh_onload_callbacks = [];\n",
       "    root._bokeh_is_loading = undefined;\n",
       "  }\n",
       "\n",
       "  var JS_MIME_TYPE = 'application/javascript';\n",
       "  var HTML_MIME_TYPE = 'text/html';\n",
       "  var EXEC_MIME_TYPE = 'application/vnd.bokehjs_exec.v0+json';\n",
       "  var CLASS_NAME = 'output_bokeh rendered_html';\n",
       "\n",
       "  /**\n",
       "   * Render data to the DOM node\n",
       "   */\n",
       "  function render(props, node) {\n",
       "    var script = document.createElement(\"script\");\n",
       "    node.appendChild(script);\n",
       "  }\n",
       "\n",
       "  /**\n",
       "   * Handle when an output is cleared or removed\n",
       "   */\n",
       "  function handleClearOutput(event, handle) {\n",
       "    var cell = handle.cell;\n",
       "\n",
       "    var id = cell.output_area._bokeh_element_id;\n",
       "    var server_id = cell.output_area._bokeh_server_id;\n",
       "    // Clean up Bokeh references\n",
       "    if (id !== undefined) {\n",
       "      Bokeh.index[id].model.document.clear();\n",
       "      delete Bokeh.index[id];\n",
       "    }\n",
       "\n",
       "    if (server_id !== undefined) {\n",
       "      // Clean up Bokeh references\n",
       "      var cmd = \"from bokeh.io.state import curstate; print(curstate().uuid_to_server['\" + server_id + \"'].get_sessions()[0].document.roots[0]._id)\";\n",
       "      cell.notebook.kernel.execute(cmd, {\n",
       "        iopub: {\n",
       "          output: function(msg) {\n",
       "            var element_id = msg.content.text.trim();\n",
       "            Bokeh.index[element_id].model.document.clear();\n",
       "            delete Bokeh.index[element_id];\n",
       "          }\n",
       "        }\n",
       "      });\n",
       "      // Destroy server and session\n",
       "      var cmd = \"import bokeh.io.notebook as ion; ion.destroy_server('\" + server_id + \"')\";\n",
       "      cell.notebook.kernel.execute(cmd);\n",
       "    }\n",
       "  }\n",
       "\n",
       "  /**\n",
       "   * Handle when a new output is added\n",
       "   */\n",
       "  function handleAddOutput(event, handle) {\n",
       "    var output_area = handle.output_area;\n",
       "    var output = handle.output;\n",
       "\n",
       "    // limit handleAddOutput to display_data with EXEC_MIME_TYPE content only\n",
       "    if ((output.output_type != \"display_data\") || (!output.data.hasOwnProperty(EXEC_MIME_TYPE))) {\n",
       "      return\n",
       "    }\n",
       "\n",
       "    var toinsert = output_area.element.find(\".\" + CLASS_NAME.split(' ')[0]);\n",
       "\n",
       "    if (output.metadata[EXEC_MIME_TYPE][\"id\"] !== undefined) {\n",
       "      toinsert[0].firstChild.textContent = output.data[JS_MIME_TYPE];\n",
       "      // store reference to embed id on output_area\n",
       "      output_area._bokeh_element_id = output.metadata[EXEC_MIME_TYPE][\"id\"];\n",
       "    }\n",
       "    if (output.metadata[EXEC_MIME_TYPE][\"server_id\"] !== undefined) {\n",
       "      var bk_div = document.createElement(\"div\");\n",
       "      bk_div.innerHTML = output.data[HTML_MIME_TYPE];\n",
       "      var script_attrs = bk_div.children[0].attributes;\n",
       "      for (var i = 0; i < script_attrs.length; i++) {\n",
       "        toinsert[0].firstChild.setAttribute(script_attrs[i].name, script_attrs[i].value);\n",
       "      }\n",
       "      // store reference to server id on output_area\n",
       "      output_area._bokeh_server_id = output.metadata[EXEC_MIME_TYPE][\"server_id\"];\n",
       "    }\n",
       "  }\n",
       "\n",
       "  function register_renderer(events, OutputArea) {\n",
       "\n",
       "    function append_mime(data, metadata, element) {\n",
       "      // create a DOM node to render to\n",
       "      var toinsert = this.create_output_subarea(\n",
       "        metadata,\n",
       "        CLASS_NAME,\n",
       "        EXEC_MIME_TYPE\n",
       "      );\n",
       "      this.keyboard_manager.register_events(toinsert);\n",
       "      // Render to node\n",
       "      var props = {data: data, metadata: metadata[EXEC_MIME_TYPE]};\n",
       "      render(props, toinsert[0]);\n",
       "      element.append(toinsert);\n",
       "      return toinsert\n",
       "    }\n",
       "\n",
       "    /* Handle when an output is cleared or removed */\n",
       "    events.on('clear_output.CodeCell', handleClearOutput);\n",
       "    events.on('delete.Cell', handleClearOutput);\n",
       "\n",
       "    /* Handle when a new output is added */\n",
       "    events.on('output_added.OutputArea', handleAddOutput);\n",
       "\n",
       "    /**\n",
       "     * Register the mime type and append_mime function with output_area\n",
       "     */\n",
       "    OutputArea.prototype.register_mime_type(EXEC_MIME_TYPE, append_mime, {\n",
       "      /* Is output safe? */\n",
       "      safe: true,\n",
       "      /* Index of renderer in `output_area.display_order` */\n",
       "      index: 0\n",
       "    });\n",
       "  }\n",
       "\n",
       "  // register the mime type if in Jupyter Notebook environment and previously unregistered\n",
       "  if (root.Jupyter !== undefined) {\n",
       "    var events = require('base/js/events');\n",
       "    var OutputArea = require('notebook/js/outputarea').OutputArea;\n",
       "\n",
       "    if (OutputArea.prototype.mime_types().indexOf(EXEC_MIME_TYPE) == -1) {\n",
       "      register_renderer(events, OutputArea);\n",
       "    }\n",
       "  }\n",
       "\n",
       "  \n",
       "  if (typeof (root._bokeh_timeout) === \"undefined\" || force === true) {\n",
       "    root._bokeh_timeout = Date.now() + 5000;\n",
       "    root._bokeh_failed_load = false;\n",
       "  }\n",
       "\n",
       "  var NB_LOAD_WARNING = {'data': {'text/html':\n",
       "     \"<div style='background-color: #fdd'>\\n\"+\n",
       "     \"<p>\\n\"+\n",
       "     \"BokehJS does not appear to have successfully loaded. If loading BokehJS from CDN, this \\n\"+\n",
       "     \"may be due to a slow or bad network connection. Possible fixes:\\n\"+\n",
       "     \"</p>\\n\"+\n",
       "     \"<ul>\\n\"+\n",
       "     \"<li>re-rerun `output_notebook()` to attempt to load from CDN again, or</li>\\n\"+\n",
       "     \"<li>use INLINE resources instead, as so:</li>\\n\"+\n",
       "     \"</ul>\\n\"+\n",
       "     \"<code>\\n\"+\n",
       "     \"from bokeh.resources import INLINE\\n\"+\n",
       "     \"output_notebook(resources=INLINE)\\n\"+\n",
       "     \"</code>\\n\"+\n",
       "     \"</div>\"}};\n",
       "\n",
       "  function display_loaded() {\n",
       "    var el = document.getElementById(\"536c3786-9b17-48e4-bdf7-84f09eee2705\");\n",
       "    if (el != null) {\n",
       "      el.textContent = \"BokehJS is loading...\";\n",
       "    }\n",
       "    if (root.Bokeh !== undefined) {\n",
       "      if (el != null) {\n",
       "        el.textContent = \"BokehJS \" + root.Bokeh.version + \" successfully loaded.\";\n",
       "      }\n",
       "    } else if (Date.now() < root._bokeh_timeout) {\n",
       "      setTimeout(display_loaded, 100)\n",
       "    }\n",
       "  }\n",
       "\n",
       "\n",
       "  function run_callbacks() {\n",
       "    try {\n",
       "      root._bokeh_onload_callbacks.forEach(function(callback) { callback() });\n",
       "    }\n",
       "    finally {\n",
       "      delete root._bokeh_onload_callbacks\n",
       "    }\n",
       "    console.info(\"Bokeh: all callbacks have finished\");\n",
       "  }\n",
       "\n",
       "  function load_libs(js_urls, callback) {\n",
       "    root._bokeh_onload_callbacks.push(callback);\n",
       "    if (root._bokeh_is_loading > 0) {\n",
       "      console.log(\"Bokeh: BokehJS is being loaded, scheduling callback at\", now());\n",
       "      return null;\n",
       "    }\n",
       "    if (js_urls == null || js_urls.length === 0) {\n",
       "      run_callbacks();\n",
       "      return null;\n",
       "    }\n",
       "    console.log(\"Bokeh: BokehJS not loaded, scheduling load and callback at\", now());\n",
       "    root._bokeh_is_loading = js_urls.length;\n",
       "    for (var i = 0; i < js_urls.length; i++) {\n",
       "      var url = js_urls[i];\n",
       "      var s = document.createElement('script');\n",
       "      s.src = url;\n",
       "      s.async = false;\n",
       "      s.onreadystatechange = s.onload = function() {\n",
       "        root._bokeh_is_loading--;\n",
       "        if (root._bokeh_is_loading === 0) {\n",
       "          console.log(\"Bokeh: all BokehJS libraries loaded\");\n",
       "          run_callbacks()\n",
       "        }\n",
       "      };\n",
       "      s.onerror = function() {\n",
       "        console.warn(\"failed to load library \" + url);\n",
       "      };\n",
       "      console.log(\"Bokeh: injecting script tag for BokehJS library: \", url);\n",
       "      document.getElementsByTagName(\"head\")[0].appendChild(s);\n",
       "    }\n",
       "  };var element = document.getElementById(\"536c3786-9b17-48e4-bdf7-84f09eee2705\");\n",
       "  if (element == null) {\n",
       "    console.log(\"Bokeh: ERROR: autoload.js configured with elementid '536c3786-9b17-48e4-bdf7-84f09eee2705' but no matching script tag was found. \")\n",
       "    return false;\n",
       "  }\n",
       "\n",
       "  var js_urls = [\"https://cdn.pydata.org/bokeh/release/bokeh-0.12.13.min.js\", \"https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.13.min.js\", \"https://cdn.pydata.org/bokeh/release/bokeh-tables-0.12.13.min.js\", \"https://cdn.pydata.org/bokeh/release/bokeh-gl-0.12.13.min.js\"];\n",
       "\n",
       "  var inline_js = [\n",
       "    function(Bokeh) {\n",
       "      Bokeh.set_log_level(\"info\");\n",
       "    },\n",
       "    \n",
       "    function(Bokeh) {\n",
       "      \n",
       "    },\n",
       "    function(Bokeh) {\n",
       "      console.log(\"Bokeh: injecting CSS: https://cdn.pydata.org/bokeh/release/bokeh-0.12.13.min.css\");\n",
       "      Bokeh.embed.inject_css(\"https://cdn.pydata.org/bokeh/release/bokeh-0.12.13.min.css\");\n",
       "      console.log(\"Bokeh: injecting CSS: https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.13.min.css\");\n",
       "      Bokeh.embed.inject_css(\"https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.13.min.css\");\n",
       "      console.log(\"Bokeh: injecting CSS: https://cdn.pydata.org/bokeh/release/bokeh-tables-0.12.13.min.css\");\n",
       "      Bokeh.embed.inject_css(\"https://cdn.pydata.org/bokeh/release/bokeh-tables-0.12.13.min.css\");\n",
       "    }\n",
       "  ];\n",
       "\n",
       "  function run_inline_js() {\n",
       "    \n",
       "    if ((root.Bokeh !== undefined) || (force === true)) {\n",
       "      for (var i = 0; i < inline_js.length; i++) {\n",
       "        inline_js[i].call(root, root.Bokeh);\n",
       "      }if (force === true) {\n",
       "        display_loaded();\n",
       "      }} else if (Date.now() < root._bokeh_timeout) {\n",
       "      setTimeout(run_inline_js, 100);\n",
       "    } else if (!root._bokeh_failed_load) {\n",
       "      console.log(\"Bokeh: BokehJS failed to load within specified timeout.\");\n",
       "      root._bokeh_failed_load = true;\n",
       "    } else if (force !== true) {\n",
       "      var cell = $(document.getElementById(\"536c3786-9b17-48e4-bdf7-84f09eee2705\")).parents('.cell').data().cell;\n",
       "      cell.output_area.append_execute_result(NB_LOAD_WARNING)\n",
       "    }\n",
       "\n",
       "  }\n",
       "\n",
       "  if (root._bokeh_is_loading === 0) {\n",
       "    console.log(\"Bokeh: BokehJS loaded, going straight to plotting\");\n",
       "    run_inline_js();\n",
       "  } else {\n",
       "    load_libs(js_urls, function() {\n",
       "      console.log(\"Bokeh: BokehJS plotting callback run at\", now());\n",
       "      run_inline_js();\n",
       "    });\n",
       "  }\n",
       "}(window));"
      ],
      "application/vnd.bokehjs_load.v0+json": "\n(function(root) {\n  function now() {\n    return new Date();\n  }\n\n  var force = true;\n\n  if (typeof (root._bokeh_onload_callbacks) === \"undefined\" || force === true) {\n    root._bokeh_onload_callbacks = [];\n    root._bokeh_is_loading = undefined;\n  }\n\n  \n\n  \n  if (typeof (root._bokeh_timeout) === \"undefined\" || force === true) {\n    root._bokeh_timeout = Date.now() + 5000;\n    root._bokeh_failed_load = false;\n  }\n\n  var NB_LOAD_WARNING = {'data': {'text/html':\n     \"<div style='background-color: #fdd'>\\n\"+\n     \"<p>\\n\"+\n     \"BokehJS does not appear to have successfully loaded. If loading BokehJS from CDN, this \\n\"+\n     \"may be due to a slow or bad network connection. Possible fixes:\\n\"+\n     \"</p>\\n\"+\n     \"<ul>\\n\"+\n     \"<li>re-rerun `output_notebook()` to attempt to load from CDN again, or</li>\\n\"+\n     \"<li>use INLINE resources instead, as so:</li>\\n\"+\n     \"</ul>\\n\"+\n     \"<code>\\n\"+\n     \"from bokeh.resources import INLINE\\n\"+\n     \"output_notebook(resources=INLINE)\\n\"+\n     \"</code>\\n\"+\n     \"</div>\"}};\n\n  function display_loaded() {\n    var el = document.getElementById(\"536c3786-9b17-48e4-bdf7-84f09eee2705\");\n    if (el != null) {\n      el.textContent = \"BokehJS is loading...\";\n    }\n    if (root.Bokeh !== undefined) {\n      if (el != null) {\n        el.textContent = \"BokehJS \" + root.Bokeh.version + \" successfully loaded.\";\n      }\n    } else if (Date.now() < root._bokeh_timeout) {\n      setTimeout(display_loaded, 100)\n    }\n  }\n\n\n  function run_callbacks() {\n    try {\n      root._bokeh_onload_callbacks.forEach(function(callback) { callback() });\n    }\n    finally {\n      delete root._bokeh_onload_callbacks\n    }\n    console.info(\"Bokeh: all callbacks have finished\");\n  }\n\n  function load_libs(js_urls, callback) {\n    root._bokeh_onload_callbacks.push(callback);\n    if (root._bokeh_is_loading > 0) {\n      console.log(\"Bokeh: BokehJS is being loaded, scheduling callback at\", now());\n      return null;\n    }\n    if (js_urls == null || js_urls.length === 0) {\n      run_callbacks();\n      return null;\n    }\n    console.log(\"Bokeh: BokehJS not loaded, scheduling load and callback at\", now());\n    root._bokeh_is_loading = js_urls.length;\n    for (var i = 0; i < js_urls.length; i++) {\n      var url = js_urls[i];\n      var s = document.createElement('script');\n      s.src = url;\n      s.async = false;\n      s.onreadystatechange = s.onload = function() {\n        root._bokeh_is_loading--;\n        if (root._bokeh_is_loading === 0) {\n          console.log(\"Bokeh: all BokehJS libraries loaded\");\n          run_callbacks()\n        }\n      };\n      s.onerror = function() {\n        console.warn(\"failed to load library \" + url);\n      };\n      console.log(\"Bokeh: injecting script tag for BokehJS library: \", url);\n      document.getElementsByTagName(\"head\")[0].appendChild(s);\n    }\n  };var element = document.getElementById(\"536c3786-9b17-48e4-bdf7-84f09eee2705\");\n  if (element == null) {\n    console.log(\"Bokeh: ERROR: autoload.js configured with elementid '536c3786-9b17-48e4-bdf7-84f09eee2705' but no matching script tag was found. \")\n    return false;\n  }\n\n  var js_urls = [\"https://cdn.pydata.org/bokeh/release/bokeh-0.12.13.min.js\", \"https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.13.min.js\", \"https://cdn.pydata.org/bokeh/release/bokeh-tables-0.12.13.min.js\", \"https://cdn.pydata.org/bokeh/release/bokeh-gl-0.12.13.min.js\"];\n\n  var inline_js = [\n    function(Bokeh) {\n      Bokeh.set_log_level(\"info\");\n    },\n    \n    function(Bokeh) {\n      \n    },\n    function(Bokeh) {\n      console.log(\"Bokeh: injecting CSS: https://cdn.pydata.org/bokeh/release/bokeh-0.12.13.min.css\");\n      Bokeh.embed.inject_css(\"https://cdn.pydata.org/bokeh/release/bokeh-0.12.13.min.css\");\n      console.log(\"Bokeh: injecting CSS: https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.13.min.css\");\n      Bokeh.embed.inject_css(\"https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.13.min.css\");\n      console.log(\"Bokeh: injecting CSS: https://cdn.pydata.org/bokeh/release/bokeh-tables-0.12.13.min.css\");\n      Bokeh.embed.inject_css(\"https://cdn.pydata.org/bokeh/release/bokeh-tables-0.12.13.min.css\");\n    }\n  ];\n\n  function run_inline_js() {\n    \n    if ((root.Bokeh !== undefined) || (force === true)) {\n      for (var i = 0; i < inline_js.length; i++) {\n        inline_js[i].call(root, root.Bokeh);\n      }if (force === true) {\n        display_loaded();\n      }} else if (Date.now() < root._bokeh_timeout) {\n      setTimeout(run_inline_js, 100);\n    } else if (!root._bokeh_failed_load) {\n      console.log(\"Bokeh: BokehJS failed to load within specified timeout.\");\n      root._bokeh_failed_load = true;\n    } else if (force !== true) {\n      var cell = $(document.getElementById(\"536c3786-9b17-48e4-bdf7-84f09eee2705\")).parents('.cell').data().cell;\n      cell.output_area.append_execute_result(NB_LOAD_WARNING)\n    }\n\n  }\n\n  if (root._bokeh_is_loading === 0) {\n    console.log(\"Bokeh: BokehJS loaded, going straight to plotting\");\n    run_inline_js();\n  } else {\n    load_libs(js_urls, function() {\n      console.log(\"Bokeh: BokehJS plotting callback run at\", now());\n      run_inline_js();\n    });\n  }\n}(window));"
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "\n",
       "<div class=\"bk-root\">\n",
       "    <div class=\"bk-plotdiv\" id=\"73109147-aeed-4c81-b605-f9854341358a\"></div>\n",
       "</div>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/javascript": [
       "(function(root) {\n",
       "  function embed_document(root) {\n",
       "    \n",
       "  var docs_json = {\"412aea6f-5ecb-488d-943d-6f9517357e9f\":{\"roots\":{\"references\":[{\"attributes\":{\"plot\":{\"id\":\"57dfc872-2e52-4991-9b70-c42943f665de\",\"subtype\":\"Figure\",\"type\":\"Plot\"},\"ticker\":{\"id\":\"18bbdc6b-5306-42ba-87ac-fefbfffbf151\",\"type\":\"BasicTicker\"}},\"id\":\"4830f4a7-e24c-4428-b396-71c88c208e63\",\"type\":\"Grid\"},{\"attributes\":{\"below\":[{\"id\":\"6911c8ff-258d-4fa0-87a2-56dc11d113ff\",\"type\":\"LinearAxis\"}],\"left\":[{\"id\":\"a8360473-e125-4be2-9e58-af3f6991a3c3\",\"type\":\"LinearAxis\"}],\"renderers\":[{\"id\":\"6911c8ff-258d-4fa0-87a2-56dc11d113ff\",\"type\":\"LinearAxis\"},{\"id\":\"4830f4a7-e24c-4428-b396-71c88c208e63\",\"type\":\"Grid\"},{\"id\":\"a8360473-e125-4be2-9e58-af3f6991a3c3\",\"type\":\"LinearAxis\"},{\"id\":\"56368c55-d3d7-4725-acb2-e362a3c85c49\",\"type\":\"Grid\"},{\"id\":\"65d4034c-5720-4a08-9504-31633a1e3b03\",\"type\":\"BoxAnnotation\"},{\"id\":\"ba99dd31-3131-46cb-9d40-655eeacecfba\",\"type\":\"GlyphRenderer\"}],\"title\":{\"id\":\"b57311b1-805c-468e-b609-81f5be805b32\",\"type\":\"Title\"},\"toolbar\":{\"id\":\"eae4922a-b337-41aa-995e-4151124f8a3f\",\"type\":\"Toolbar\"},\"x_range\":{\"id\":\"794ccc35-ec54-4f61-bbfe-2abc513e0a27\",\"type\":\"DataRange1d\"},\"x_scale\":{\"id\":\"248a3968-7b3f-480b-bfcd-98156513cea1\",\"type\":\"LinearScale\"},\"y_range\":{\"id\":\"2c3fd4b2-85b8-48a1-bc49-a58929c97f40\",\"type\":\"DataRange1d\"},\"y_scale\":{\"id\":\"985a4528-979c-4e00-8721-8dc37876f1c8\",\"type\":\"LinearScale\"}},\"id\":\"57dfc872-2e52-4991-9b70-c42943f665de\",\"subtype\":\"Figure\",\"type\":\"Plot\"},{\"attributes\":{\"dimension\":1,\"plot\":{\"id\":\"57dfc872-2e52-4991-9b70-c42943f665de\",\"subtype\":\"Figure\",\"type\":\"Plot\"},\"ticker\":{\"id\":\"8642a8aa-ebf1-4556-83d7-c08333204f3a\",\"type\":\"BasicTicker\"}},\"id\":\"56368c55-d3d7-4725-acb2-e362a3c85c49\",\"type\":\"Grid\"},{\"attributes\":{\"plot\":null,\"text\":\"Relationship between cylinders and displacement\"},\"id\":\"b57311b1-805c-468e-b609-81f5be805b32\",\"type\":\"Title\"},{\"attributes\":{},\"id\":\"679f091f-d266-4b9b-9abe-ffe60fb10656\",\"type\":\"BasicTickFormatter\"},{\"attributes\":{\"fill_alpha\":{\"value\":0.1},\"fill_color\":{\"value\":\"#1f77b4\"},\"line_alpha\":{\"value\":0.1},\"line_color\":{\"value\":\"#1f77b4\"},\"size\":{\"units\":\"screen\",\"value\":10},\"x\":{\"field\":\"x\"},\"y\":{\"field\":\"y\"}},\"id\":\"9ecb87f3-8dce-40a7-a193-4f5378d96a2f\",\"type\":\"Circle\"},{\"attributes\":{},\"id\":\"f4058a17-97ea-4354-b1c6-83c55fea07c4\",\"type\":\"BasicTickFormatter\"},{\"attributes\":{\"axis_label\":\"Cylinders\",\"formatter\":{\"id\":\"679f091f-d266-4b9b-9abe-ffe60fb10656\",\"type\":\"BasicTickFormatter\"},\"plot\":{\"id\":\"57dfc872-2e52-4991-9b70-c42943f665de\",\"subtype\":\"Figure\",\"type\":\"Plot\"},\"ticker\":{\"id\":\"18bbdc6b-5306-42ba-87ac-fefbfffbf151\",\"type\":\"BasicTicker\"}},\"id\":\"6911c8ff-258d-4fa0-87a2-56dc11d113ff\",\"type\":\"LinearAxis\"},{\"attributes\":{\"overlay\":{\"id\":\"65d4034c-5720-4a08-9504-31633a1e3b03\",\"type\":\"BoxAnnotation\"}},\"id\":\"cff2b3ce-3311-4f6c-8f87-7c033693273d\",\"type\":\"BoxZoomTool\"},{\"attributes\":{},\"id\":\"23d80c0d-5603-4efe-bc6f-6e665a93d283\",\"type\":\"SaveTool\"},{\"attributes\":{},\"id\":\"b2584991-fa32-440d-a01b-8e6a914a85bc\",\"type\":\"ResetTool\"},{\"attributes\":{},\"id\":\"248a3968-7b3f-480b-bfcd-98156513cea1\",\"type\":\"LinearScale\"},{\"attributes\":{},\"id\":\"8642a8aa-ebf1-4556-83d7-c08333204f3a\",\"type\":\"BasicTicker\"},{\"attributes\":{},\"id\":\"93201585-d243-471d-ae84-f4b8602ee243\",\"type\":\"HelpTool\"},{\"attributes\":{},\"id\":\"985a4528-979c-4e00-8721-8dc37876f1c8\",\"type\":\"LinearScale\"},{\"attributes\":{\"bottom_units\":\"screen\",\"fill_alpha\":{\"value\":0.5},\"fill_color\":{\"value\":\"lightgrey\"},\"left_units\":\"screen\",\"level\":\"overlay\",\"line_alpha\":{\"value\":1.0},\"line_color\":{\"value\":\"black\"},\"line_dash\":[4,4],\"line_width\":{\"value\":2},\"plot\":null,\"render_mode\":\"css\",\"right_units\":\"screen\",\"top_units\":\"screen\"},\"id\":\"65d4034c-5720-4a08-9504-31633a1e3b03\",\"type\":\"BoxAnnotation\"},{\"attributes\":{\"source\":{\"id\":\"13fbdfad-d153-4f83-9ec9-4e11e20acc0f\",\"type\":\"ColumnDataSource\"}},\"id\":\"61084ca0-ddaa-40ba-8dd8-ae4e180d92e2\",\"type\":\"CDSView\"},{\"attributes\":{\"callback\":null,\"column_names\":[\"x\",\"y\"],\"data\":{\"x\":[3,8,8,6,8,6,4,12,4,8,5,4,6,6,4,6],\"y\":[2.0,4.4,4.7,3.6,5.6,3.0,2.5,6.0,2.0,5.3,2.7,2.0,3.3,3.0,2.0,3.0]}},\"id\":\"13fbdfad-d153-4f83-9ec9-4e11e20acc0f\",\"type\":\"ColumnDataSource\"},{\"attributes\":{\"active_drag\":\"auto\",\"active_inspect\":\"auto\",\"active_scroll\":\"auto\",\"active_tap\":\"auto\",\"tools\":[{\"id\":\"99983b37-9485-44cb-8c14-d23443634f83\",\"type\":\"PanTool\"},{\"id\":\"676f3d28-45cd-44d2-994c-eaa06013211b\",\"type\":\"WheelZoomTool\"},{\"id\":\"cff2b3ce-3311-4f6c-8f87-7c033693273d\",\"type\":\"BoxZoomTool\"},{\"id\":\"23d80c0d-5603-4efe-bc6f-6e665a93d283\",\"type\":\"SaveTool\"},{\"id\":\"b2584991-fa32-440d-a01b-8e6a914a85bc\",\"type\":\"ResetTool\"},{\"id\":\"93201585-d243-471d-ae84-f4b8602ee243\",\"type\":\"HelpTool\"}]},\"id\":\"eae4922a-b337-41aa-995e-4151124f8a3f\",\"type\":\"Toolbar\"},{\"attributes\":{},\"id\":\"676f3d28-45cd-44d2-994c-eaa06013211b\",\"type\":\"WheelZoomTool\"},{\"attributes\":{\"fill_alpha\":{\"value\":0.2},\"fill_color\":{\"value\":\"#1f77b4\"},\"line_color\":{\"value\":\"#1f77b4\"},\"size\":{\"units\":\"screen\",\"value\":10},\"x\":{\"field\":\"x\"},\"y\":{\"field\":\"y\"}},\"id\":\"c924c201-4dd8-42d5-9d32-39c22430756a\",\"type\":\"Circle\"},{\"attributes\":{},\"id\":\"18bbdc6b-5306-42ba-87ac-fefbfffbf151\",\"type\":\"BasicTicker\"},{\"attributes\":{\"callback\":null},\"id\":\"2c3fd4b2-85b8-48a1-bc49-a58929c97f40\",\"type\":\"DataRange1d\"},{\"attributes\":{},\"id\":\"99983b37-9485-44cb-8c14-d23443634f83\",\"type\":\"PanTool\"},{\"attributes\":{\"data_source\":{\"id\":\"13fbdfad-d153-4f83-9ec9-4e11e20acc0f\",\"type\":\"ColumnDataSource\"},\"glyph\":{\"id\":\"c924c201-4dd8-42d5-9d32-39c22430756a\",\"type\":\"Circle\"},\"hover_glyph\":null,\"muted_glyph\":null,\"nonselection_glyph\":{\"id\":\"9ecb87f3-8dce-40a7-a193-4f5378d96a2f\",\"type\":\"Circle\"},\"selection_glyph\":null,\"view\":{\"id\":\"61084ca0-ddaa-40ba-8dd8-ae4e180d92e2\",\"type\":\"CDSView\"}},\"id\":\"ba99dd31-3131-46cb-9d40-655eeacecfba\",\"type\":\"GlyphRenderer\"},{\"attributes\":{\"axis_label\":\"Displacement\",\"formatter\":{\"id\":\"f4058a17-97ea-4354-b1c6-83c55fea07c4\",\"type\":\"BasicTickFormatter\"},\"plot\":{\"id\":\"57dfc872-2e52-4991-9b70-c42943f665de\",\"subtype\":\"Figure\",\"type\":\"Plot\"},\"ticker\":{\"id\":\"8642a8aa-ebf1-4556-83d7-c08333204f3a\",\"type\":\"BasicTicker\"}},\"id\":\"a8360473-e125-4be2-9e58-af3f6991a3c3\",\"type\":\"LinearAxis\"},{\"attributes\":{\"callback\":null},\"id\":\"794ccc35-ec54-4f61-bbfe-2abc513e0a27\",\"type\":\"DataRange1d\"}],\"root_ids\":[\"57dfc872-2e52-4991-9b70-c42943f665de\"]},\"title\":\"Bokeh Application\",\"version\":\"0.12.13\"}};\n",
       "  var render_items = [{\"docid\":\"412aea6f-5ecb-488d-943d-6f9517357e9f\",\"elementid\":\"73109147-aeed-4c81-b605-f9854341358a\",\"modelid\":\"57dfc872-2e52-4991-9b70-c42943f665de\"}];\n",
       "  root.Bokeh.embed.embed_items_notebook(docs_json, render_items);\n",
       "\n",
       "  }\n",
       "  if (root.Bokeh !== undefined) {\n",
       "    embed_document(root);\n",
       "  } else {\n",
       "    var attempts = 0;\n",
       "    var timer = setInterval(function(root) {\n",
       "      if (root.Bokeh !== undefined) {\n",
       "        embed_document(root);\n",
       "        clearInterval(timer);\n",
       "      }\n",
       "      attempts++;\n",
       "      if (attempts > 100) {\n",
       "        console.log(\"Bokeh: ERROR: Unable to run BokehJS code because BokehJS library is missing\")\n",
       "        clearInterval(timer);\n",
       "      }\n",
       "    }, 10, root)\n",
       "  }\n",
       "})(window);"
      ],
      "application/vnd.bokehjs_exec.v0+json": ""
     },
     "metadata": {
      "application/vnd.bokehjs_exec.v0+json": {
       "id": "57dfc872-2e52-4991-9b70-c42943f665de"
      }
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "%%local \n",
    "from bokeh.io import show\n",
    "from bokeh.plotting import figure\n",
    "from bokeh.io import output_notebook\n",
    "output_notebook()\n",
    "p = figure(title = 'Relationship between cylinders and displacement')\n",
    "p.xaxis.axis_label = 'Cylinders'\n",
    "p.yaxis.axis_label = 'Displacement'\n",
    "\n",
    "p.circle(  list(scatter_source['Cylinders'])\n",
    "         , list(scatter_source['Displacement'])\n",
    "         , fill_alpha=0.2, size=10)\n",
    "\n",
    "show(p)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "PySpark3",
   "language": "",
   "name": "pyspark3kernel"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "python",
    "version": 3
   },
   "mimetype": "text/x-python",
   "name": "pyspark3",
   "pygments_lexer": "python3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
